Source for file mssql.php

Documentation is available at mssql.php

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  4.  
  5. /**
  6.  * The PEAR DB driver for PHP's mssql extension
  7.  * for interacting with Microsoft SQL Server 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     Sterling Hughes <sterling@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: mssql.php,v 1.92 2007/09/21 13:40:41 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_PEAR_PATH.'DB/common.php';
  31.  
  32. /**
  33.  * The methods PEAR DB uses to interact with PHP's mssql extension
  34.  * for interacting with Microsoft SQL Server databases
  35.  *
  36.  * These methods overload the ones declared in DB_common.
  37.  *
  38.  * DB's mssql driver is only for Microsfoft SQL Server databases.
  39.  *
  40.  * If you're connecting to a Sybase database, you MUST specify "sybase"
  41.  * as the "phptype" in the DSN.
  42.  *
  43.  * This class only works correctly if you have compiled PHP using
  44.  * --with-mssql=[dir_to_FreeTDS].
  45.  *
  46.  * @category   Database
  47.  * @package    DB
  48.  * @author     Sterling Hughes <sterling@php.net>
  49.  * @author     Daniel Convissor <danielc@php.net>
  50.  * @copyright  1997-2007 The PHP Group
  51.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  52.  * @version    Release: 1.7.13
  53.  * @link       http://pear.php.net/package/DB
  54.  */
  55. class DB_mssql extends DB_common
  56. {
  57.     // {{{ properties
  58.  
  59.     /**
  60.      * The DB driver type (mysql, oci8, odbc, etc.)
  61.      * @var string 
  62.      */
  63.     var $phptype = 'mssql';
  64.  
  65.     /**
  66.      * The database syntax variant to be used (db2, access, etc.), if any
  67.      * @var string 
  68.      */
  69.     var $dbsyntax = 'mssql';
  70.  
  71.     /**
  72.      * The capabilities of this DB implementation
  73.      *
  74.      * The 'new_link' element contains the PHP version that first provided
  75.      * new_link support for this DBMS.  Contains false if it's unsupported.
  76.      *
  77.      * Meaning of the 'limit' element:
  78.      *   + 'emulate' = emulate with fetch row by number
  79.      *   + 'alter'   = alter the query
  80.      *   + false     = skip rows
  81.      *
  82.      * @var array 
  83.      */
  84.     var $features = array(
  85.         'limit'         => 'emulate',
  86.         'new_link'      => false,
  87.         'numrows'       => true,
  88.         'pconnect'      => true,
  89.         'prepare'       => false,
  90.         'ssl'           => false,
  91.         'transactions'  => true,
  92.     );
  93.  
  94.     /**
  95.      * A mapping of native error codes to DB error codes
  96.      * @var array 
  97.      */
  98.     // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX
  99.     var $errorcode_map = array(
  100.         102   => DB_ERROR_SYNTAX,
  101.         110   => DB_ERROR_VALUE_COUNT_ON_ROW,
  102.         155   => DB_ERROR_NOSUCHFIELD,
  103.         156   => DB_ERROR_SYNTAX,
  104.         170   => DB_ERROR_SYNTAX,
  105.         207   => DB_ERROR_NOSUCHFIELD,
  106.         208   => DB_ERROR_NOSUCHTABLE,
  107.         245   => DB_ERROR_INVALID_NUMBER,
  108.         319   => DB_ERROR_SYNTAX,
  109.         321   => DB_ERROR_NOSUCHFIELD,
  110.         325   => DB_ERROR_SYNTAX,
  111.         336   => DB_ERROR_SYNTAX,
  112.         515   => DB_ERROR_CONSTRAINT_NOT_NULL,
  113.         547   => DB_ERROR_CONSTRAINT,
  114.         1018  => DB_ERROR_SYNTAX,
  115.         1035  => DB_ERROR_SYNTAX,
  116.         1913  => DB_ERROR_ALREADY_EXISTS,
  117.         2209  => DB_ERROR_SYNTAX,
  118.         2223  => DB_ERROR_SYNTAX,
  119.         2248  => DB_ERROR_SYNTAX,
  120.         2256  => DB_ERROR_SYNTAX,
  121.         2257  => DB_ERROR_SYNTAX,
  122.         2627  => DB_ERROR_CONSTRAINT,
  123.         2714  => DB_ERROR_ALREADY_EXISTS,
  124.         3607  => DB_ERROR_DIVZERO,
  125.         3701  => DB_ERROR_NOSUCHTABLE,
  126.         7630  => DB_ERROR_SYNTAX,
  127.         8134  => DB_ERROR_DIVZERO,
  128.         9303  => DB_ERROR_SYNTAX,
  129.         9317  => DB_ERROR_SYNTAX,
  130.         9318  => DB_ERROR_SYNTAX,
  131.         9331  => DB_ERROR_SYNTAX,
  132.         9332  => DB_ERROR_SYNTAX,
  133.         15253 => DB_ERROR_SYNTAX,
  134.     );
  135.  
  136.     /**
  137.      * The raw database connection created by PHP
  138.      * @var resource 
  139.      */
  140.     var $connection;
  141.  
  142.     /**
  143.      * The DSN information for connecting to a database
  144.      * @var array 
  145.      */
  146.     var $dsn = array();
  147.  
  148.  
  149.     /**
  150.      * Should data manipulation queries be committed automatically?
  151.      * @var bool 
  152.      * @access private
  153.      */
  154.     var $autocommit true;
  155.  
  156.     /**
  157.      * The quantity of transactions begun
  158.      *
  159.      * {@internal  While this is private, it can't actually be designated
  160.      * private in PHP 5 because it is directly accessed in the test suite.}}}
  161.      *
  162.      * @var integer 
  163.      * @access private
  164.      */
  165.     var $transaction_opcount 0;
  166.  
  167.     /**
  168.      * The database specified in the DSN
  169.      *
  170.      * It's a fix to allow calls to different databases in the same script.
  171.      *
  172.      * @var string 
  173.      * @access private
  174.      */
  175.     var $_db null;
  176.  
  177.  
  178.     // }}}
  179.     // {{{ constructor
  180.  
  181.     /**
  182.      * This constructor calls <kbd>$this->DB_common()</kbd>
  183.      *
  184.      * @return void 
  185.      */
  186.     function DB_mssql()
  187.     {
  188.         $this->DB_common();
  189.     }
  190.  
  191.     // }}}
  192.     // {{{ connect()
  193.  
  194.     /**
  195.      * Connect to the database server, log in and open the database
  196.      *
  197.      * Don't call this method directly.  Use DB::connect() instead.
  198.      *
  199.      * @param array $dsn         the data source name
  200.      * @param bool  $persistent  should the connection be persistent?
  201.      *
  202.      * @return int  DB_OK on success. A DB_Error object on failure.
  203.      */
  204.     function connect($dsn$persistent false)
  205.     {
  206.         if (!PEAR::loadExtension('mssql'&& !PEAR::loadExtension('sybase')
  207.             && !PEAR::loadExtension('sybase_ct'))
  208.         {
  209.             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  210.         }
  211.  
  212.         $this->dsn = $dsn;
  213.         if ($dsn['dbsyntax']{
  214.             $this->dbsyntax = $dsn['dbsyntax'];
  215.         }
  216.  
  217.         $params array(
  218.             $dsn['hostspec'$dsn['hostspec''localhost',
  219.             $dsn['username'$dsn['username'null,
  220.             $dsn['password'$dsn['password'null,
  221.         );
  222.         if ($dsn['port']{
  223.             $params[0.= ((substr(PHP_OS03== 'WIN'',' ':')
  224.                         . $dsn['port'];
  225.         }
  226.  
  227.         $connect_function $persistent 'mssql_pconnect' 'mssql_connect';
  228.  
  229.         $this->connection = @call_user_func_array($connect_function$params);
  230.  
  231.         if (!$this->connection{
  232.             return $this->raiseError(DB_ERROR_CONNECT_FAILED,
  233.                                      nullnullnull,
  234.                                      @mssql_get_last_message());
  235.         }
  236.         if ($dsn['database']{
  237.             if (!@mssql_select_db($dsn['database']$this->connection)) {
  238.                 return $this->raiseError(DB_ERROR_NODBSELECTED,
  239.                                          nullnullnull,
  240.                                          @mssql_get_last_message());
  241.             }
  242.             $this->_db $dsn['database'];
  243.         }
  244.         return DB_OK;
  245.     }
  246.  
  247.     // }}}
  248.     // {{{ disconnect()
  249.  
  250.     /**
  251.      * Disconnects from the database server
  252.      *
  253.      * @return bool  TRUE on success, FALSE on failure
  254.      */
  255.     function disconnect()
  256.     {
  257.         $ret @mssql_close($this->connection);
  258.         $this->connection = null;
  259.         return $ret;
  260.     }
  261.  
  262.     // }}}
  263.     // {{{ simpleQuery()
  264.  
  265.     /**
  266.      * Sends a query to the database server
  267.      *
  268.      * @param string  the SQL query string
  269.      *
  270.      * @return mixed  + a PHP result resrouce for successful SELECT queries
  271.      *                 + the DB_OK constant for other successful queries
  272.      *                 + a DB_Error object on failure
  273.      */
  274.     function simpleQuery($query)
  275.     {
  276.         $ismanip $this->_checkManip($query);
  277.         $this->last_query = $query;
  278.         if (!@mssql_select_db($this->_db$this->connection)) {
  279.             return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  280.         }
  281.         $query $this->modifyQuery($query);
  282.         if (!$this->autocommit && $ismanip{
  283.             if ($this->transaction_opcount == 0{
  284.                 $result @mssql_query('BEGIN TRAN'$this->connection);
  285.                 if (!$result{
  286.                     return $this->mssqlRaiseError();
  287.                 }
  288.             }
  289.             $this->transaction_opcount++;
  290.         }
  291.         $result @mssql_query($query$this->connection);
  292.         if (!$result{
  293.             return $this->mssqlRaiseError();
  294.         }
  295.         // Determine which queries that should return data, and which
  296.         // should return an error code only.
  297.         return $ismanip DB_OK $result;
  298.     }
  299.  
  300.     // }}}
  301.     // {{{ nextResult()
  302.  
  303.     /**
  304.      * Move the internal mssql result pointer to the next available result
  305.      *
  306.      * @param valid fbsql result resource
  307.      *
  308.      * @access public
  309.      *
  310.      * @return true if a result is available otherwise return false
  311.      */
  312.     function nextResult($result)
  313.     {
  314.         return @mssql_next_result($result);
  315.     }
  316.  
  317.     // }}}
  318.     // {{{ fetchInto()
  319.  
  320.     /**
  321.      * Places a row from the result set into the given array
  322.      *
  323.      * Formating of the array and the data therein are configurable.
  324.      * See DB_result::fetchInto() for more information.
  325.      *
  326.      * This method is not meant to be called directly.  Use
  327.      * DB_result::fetchInto() instead.  It can't be declared "protected"
  328.      * because DB_result is a separate object.
  329.      *
  330.      * @param resource $result    the query result resource
  331.      * @param array    $arr       the referenced array to put the data in
  332.      * @param int      $fetchmode how the resulting array should be indexed
  333.      * @param int      $rownum    the row number to fetch (0 = first row)
  334.      *
  335.      * @return mixed  DB_OK on success, NULL when the end of a result set is
  336.      *                  reached or on failure
  337.      *
  338.      * @see DB_result::fetchInto()
  339.      */
  340.     function fetchInto($result&$arr$fetchmode$rownum null)
  341.     {
  342.         if ($rownum !== null{
  343.             if (!@mssql_data_seek($result$rownum)) {
  344.                 return null;
  345.             }
  346.         }
  347.         if ($fetchmode DB_FETCHMODE_ASSOC{
  348.             $arr @mssql_fetch_assoc($result);
  349.             if ($this->options['portability'DB_PORTABILITY_LOWERCASE && $arr{
  350.                 $arr array_change_key_case($arrCASE_LOWER);
  351.             }
  352.         else {
  353.             $arr @mssql_fetch_row($result);
  354.         }
  355.         if (!$arr{
  356.             return null;
  357.         }
  358.         if ($this->options['portability'DB_PORTABILITY_RTRIM{
  359.             $this->_rtrimArrayValues($arr);
  360.         }
  361.         if ($this->options['portability'DB_PORTABILITY_NULL_TO_EMPTY{
  362.             $this->_convertNullArrayValuesToEmpty($arr);
  363.         }
  364.         return DB_OK;
  365.     }
  366.  
  367.     // }}}
  368.     // {{{ freeResult()
  369.  
  370.     /**
  371.      * Deletes the result set and frees the memory occupied by the result set
  372.      *
  373.      * This method is not meant to be called directly.  Use
  374.      * DB_result::free() instead.  It can't be declared "protected"
  375.      * because DB_result is a separate object.
  376.      *
  377.      * @param resource $result  PHP's query result resource
  378.      *
  379.      * @return bool  TRUE on success, FALSE if $result is invalid
  380.      *
  381.      * @see DB_result::free()
  382.      */
  383.     function freeResult($result)
  384.     {
  385.         return is_resource($resultmssql_free_result($resultfalse;
  386.     }
  387.  
  388.     // }}}
  389.     // {{{ numCols()
  390.  
  391.     /**
  392.      * Gets the number of columns in a result set
  393.      *
  394.      * This method is not meant to be called directly.  Use
  395.      * DB_result::numCols() instead.  It can't be declared "protected"
  396.      * because DB_result is a separate object.
  397.      *
  398.      * @param resource $result  PHP's query result resource
  399.      *
  400.      * @return int  the number of columns.  A DB_Error object on failure.
  401.      *
  402.      * @see DB_result::numCols()
  403.      */
  404.     function numCols($result)
  405.     {
  406.         $cols @mssql_num_fields($result);
  407.         if (!$cols{
  408.             return $this->mssqlRaiseError();
  409.         }
  410.         return $cols;
  411.     }
  412.  
  413.     // }}}
  414.     // {{{ numRows()
  415.  
  416.     /**
  417.      * Gets the number of rows in a result set
  418.      *
  419.      * This method is not meant to be called directly.  Use
  420.      * DB_result::numRows() instead.  It can't be declared "protected"
  421.      * because DB_result is a separate object.
  422.      *
  423.      * @param resource $result  PHP's query result resource
  424.      *
  425.      * @return int  the number of rows.  A DB_Error object on failure.
  426.      *
  427.      * @see DB_result::numRows()
  428.      */
  429.     function numRows($result)
  430.     {
  431.         $rows @mssql_num_rows($result);
  432.         if ($rows === false{
  433.             return $this->mssqlRaiseError();
  434.         }
  435.         return $rows;
  436.     }
  437.  
  438.     // }}}
  439.     // {{{ autoCommit()
  440.  
  441.     /**
  442.      * Enables or disables automatic commits
  443.      *
  444.      * @param bool $onoff  true turns it on, false turns it off
  445.      *
  446.      * @return int  DB_OK on success.  A DB_Error object if the driver
  447.      *                doesn't support auto-committing transactions.
  448.      */
  449.     function autoCommit($onoff false)
  450.     {
  451.         // XXX if $this->transaction_opcount > 0, we should probably
  452.         // issue a warning here.
  453.         $this->autocommit $onoff true false;
  454.         return DB_OK;
  455.     }
  456.  
  457.     // }}}
  458.     // {{{ commit()
  459.  
  460.     /**
  461.      * Commits the current transaction
  462.      *
  463.      * @return int  DB_OK on success.  A DB_Error object on failure.
  464.      */
  465.     function commit()
  466.     {
  467.         if ($this->transaction_opcount 0{
  468.             if (!@mssql_select_db($this->_db$this->connection)) {
  469.                 return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  470.             }
  471.             $result @mssql_query('COMMIT TRAN'$this->connection);
  472.             $this->transaction_opcount 0;
  473.             if (!$result{
  474.                 return $this->mssqlRaiseError();
  475.             }
  476.         }
  477.         return DB_OK;
  478.     }
  479.  
  480.     // }}}
  481.     // {{{ rollback()
  482.  
  483.     /**
  484.      * Reverts the current transaction
  485.      *
  486.      * @return int  DB_OK on success.  A DB_Error object on failure.
  487.      */
  488.     function rollback()
  489.     {
  490.         if ($this->transaction_opcount 0{
  491.             if (!@mssql_select_db($this->_db$this->connection)) {
  492.                 return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  493.             }
  494.             $result @mssql_query('ROLLBACK TRAN'$this->connection);
  495.             $this->transaction_opcount 0;
  496.             if (!$result{
  497.                 return $this->mssqlRaiseError();
  498.             }
  499.         }
  500.         return DB_OK;
  501.     }
  502.  
  503.     // }}}
  504.     // {{{ affectedRows()
  505.  
  506.     /**
  507.      * Determines the number of rows affected by a data maniuplation query
  508.      *
  509.      * 0 is returned for queries that don't manipulate data.
  510.      *
  511.      * @return int  the number of rows.  A DB_Error object on failure.
  512.      */
  513.     function affectedRows()
  514.     {
  515.         if ($this->_last_query_manip{
  516.             $res @mssql_query('select @@rowcount'$this->connection);
  517.             if (!$res{
  518.                 return $this->mssqlRaiseError();
  519.             }
  520.             $ar @mssql_fetch_row($res);
  521.             if (!$ar{
  522.                 $result 0;
  523.             else {
  524.                 @mssql_free_result($res);
  525.                 $result $ar[0];
  526.             }
  527.         else {
  528.             $result 0;
  529.         }
  530.         return $result;
  531.     }
  532.  
  533.     // }}}
  534.     // {{{ nextId()
  535.  
  536.     /**
  537.      * Returns the next free id in a sequence
  538.      *
  539.      * @param string  $seq_name  name of the sequence
  540.      * @param boolean $ondemand  when true, the seqence is automatically
  541.      *                             created if it does not exist
  542.      *
  543.      * @return int  the next id number in the sequence.
  544.      *                A DB_Error object on failure.
  545.      *
  546.      * @see DB_common::nextID(), DB_common::getSequenceName(),
  547.      *       DB_mssql::createSequence(), DB_mssql::dropSequence()
  548.      */
  549.     function nextId($seq_name$ondemand true)
  550.     {
  551.         $seqname $this->getSequenceName($seq_name);
  552.         if (!@mssql_select_db($this->_db$this->connection)) {
  553.             return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  554.         }
  555.         $repeat 0;
  556.         do {
  557.             $this->pushErrorHandling(PEAR_ERROR_RETURN);
  558.             $result $this->query("INSERT INTO $seqname (vapor) VALUES (0)");
  559.             $this->popErrorHandling();
  560.             if ($ondemand && DB::isError($result&&
  561.                 ($result->getCode(== DB_ERROR || $result->getCode(== DB_ERROR_NOSUCHTABLE))
  562.             {
  563.                 $repeat 1;
  564.                 $result $this->createSequence($seq_name);
  565.                 if (DB::isError($result)) {
  566.                     return $this->raiseError($result);
  567.                 }
  568.             elseif (!DB::isError($result)) {
  569.                 $result $this->query("SELECT IDENT_CURRENT('$seqname')");
  570.                 if (DB::isError($result)) {
  571.                     /* Fallback code for MS SQL Server 7.0, which doesn't have
  572.                      * IDENT_CURRENT. This is *not* safe for concurrent
  573.                      * requests, and really, if you're using it, you're in a
  574.                      * world of hurt. Nevertheless, it's here to ensure BC. See
  575.                      * bug #181 for the gory details.*/
  576.                     $result $this->query("SELECT @@IDENTITY FROM $seqname");
  577.                 }
  578.                 $repeat 0;
  579.             else {
  580.                 $repeat false;
  581.             }
  582.         while ($repeat);
  583.         if (DB::isError($result)) {
  584.             return $this->raiseError($result);
  585.         }
  586.         $result $result->fetchRow(DB_FETCHMODE_ORDERED);
  587.         return $result[0];
  588.     }
  589.  
  590.     /**
  591.      * Creates a new sequence
  592.      *
  593.      * @param string $seq_name  name of the new sequence
  594.      *
  595.      * @return int  DB_OK on success.  A DB_Error object on failure.
  596.      *
  597.      * @see DB_common::createSequence(), DB_common::getSequenceName(),
  598.      *       DB_mssql::nextID(), DB_mssql::dropSequence()
  599.      */
  600.     function createSequence($seq_name)
  601.     {
  602.         return $this->query('CREATE TABLE '
  603.                             . $this->getSequenceName($seq_name)
  604.                             . ' ([id] [int] IDENTITY (1, 1) NOT NULL,'
  605.                             . ' [vapor] [int] NULL)');
  606.     }
  607.  
  608.     // }}}
  609.     // {{{ dropSequence()
  610.  
  611.     /**
  612.      * Deletes a sequence
  613.      *
  614.      * @param string $seq_name  name of the sequence to be deleted
  615.      *
  616.      * @return int  DB_OK on success.  A DB_Error object on failure.
  617.      *
  618.      * @see DB_common::dropSequence(), DB_common::getSequenceName(),
  619.      *       DB_mssql::nextID(), DB_mssql::createSequence()
  620.      */
  621.     function dropSequence($seq_name)
  622.     {
  623.         return $this->query('DROP TABLE ' $this->getSequenceName($seq_name));
  624.     }
  625.  
  626.     // }}}
  627.     // {{{ quoteIdentifier()
  628.  
  629.     /**
  630.      * Quotes a string so it can be safely used as a table or column name
  631.      *
  632.      * @param string $str  identifier name to be quoted
  633.      *
  634.      * @return string  quoted identifier string
  635.      *
  636.      * @see DB_common::quoteIdentifier()
  637.      * @since Method available since Release 1.6.0
  638.      */
  639.     function quoteIdentifier($str)
  640.     {
  641.         return '[' str_replace(']'']]'$str']';
  642.     }
  643.  
  644.     // }}}
  645.     // {{{ mssqlRaiseError()
  646.  
  647.     /**
  648.      * Produces a DB_Error object regarding the current problem
  649.      *
  650.      * @param int $errno  if the error is being manually raised pass a
  651.      *                      DB_ERROR* constant here.  If this isn't passed
  652.      *                      the error information gathered from the DBMS.
  653.      *
  654.      * @return object  the DB_Error object
  655.      *
  656.      * @see DB_common::raiseError(),
  657.      *       DB_mssql::errorNative(), DB_mssql::errorCode()
  658.      */
  659.     function mssqlRaiseError($code null)
  660.     {
  661.         $message @mssql_get_last_message();
  662.         if (!$code{
  663.             $code $this->errorNative();
  664.         }
  665.         return $this->raiseError($this->errorCode($code$message),
  666.                                  nullnullnull"$code - $message");
  667.     }
  668.  
  669.     // }}}
  670.     // {{{ errorNative()
  671.  
  672.     /**
  673.      * Gets the DBMS' native error code produced by the last query
  674.      *
  675.      * @return int  the DBMS' error code
  676.      */
  677.     function errorNative()
  678.     {
  679.         $res @mssql_query('select @@ERROR as ErrorCode'$this->connection);
  680.         if (!$res{
  681.             return DB_ERROR;
  682.         }
  683.         $row @mssql_fetch_row($res);
  684.         return $row[0];
  685.     }
  686.  
  687.     // }}}
  688.     // {{{ errorCode()
  689.  
  690.     /**
  691.      * Determines PEAR::DB error code from mssql's native codes.
  692.      *
  693.      * If <var>$nativecode</var> isn't known yet, it will be looked up.
  694.      *
  695.      * @param  mixed  $nativecode  mssql error code, if known
  696.      * @return integer  an error number from a DB error constant
  697.      * @see errorNative()
  698.      */
  699.     function errorCode($nativecode null$msg '')
  700.     {
  701.         if (!$nativecode{
  702.             $nativecode $this->errorNative();
  703.         }
  704.         if (isset($this->errorcode_map[$nativecode])) {
  705.             if ($nativecode == 3701
  706.                 && preg_match('/Cannot drop the index/i'$msg))
  707.             {
  708.                 return DB_ERROR_NOT_FOUND;
  709.             }
  710.             return $this->errorcode_map[$nativecode];
  711.         else {
  712.             return DB_ERROR;
  713.         }
  714.     }
  715.  
  716.     // }}}
  717.     // {{{ tableInfo()
  718.  
  719.     /**
  720.      * Returns information about a table or a result set
  721.      *
  722.      * NOTE: only supports 'table' and 'flags' if <var>$result</var>
  723.      * is a table name.
  724.      *
  725.      * @param object|string $result  DB_result object from a query or a
  726.      *                                  string containing the name of a table.
  727.      *                                  While this also accepts a query result
  728.      *                                  resource identifier, this behavior is
  729.      *                                  deprecated.
  730.      * @param int            $mode    a valid tableInfo mode
  731.      *
  732.      * @return array  an associative array with the information requested.
  733.      *                  A DB_Error object on failure.
  734.      *
  735.      * @see DB_common::tableInfo()
  736.      */
  737.     function tableInfo($result$mode null)
  738.     {
  739.         if (is_string($result)) {
  740.             /*
  741.              * Probably received a table name.
  742.              * Create a result resource identifier.
  743.              */
  744.             if (!@mssql_select_db($this->_db$this->connection)) {
  745.                 return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  746.             }
  747.             $id @mssql_query("SELECT * FROM $result WHERE 1=0",
  748.                                $this->connection);
  749.             $got_string true;
  750.         elseif (isset($result->result)) {
  751.             /*
  752.              * Probably received a result object.
  753.              * Extract the result resource identifier.
  754.              */
  755.             $id $result->result;
  756.             $got_string false;
  757.         else {
  758.             /*
  759.              * Probably received a result resource identifier.
  760.              * Copy it.
  761.              * Deprecated.  Here for compatibility only.
  762.              */
  763.             $id $result;
  764.             $got_string false;
  765.         }
  766.  
  767.         if (!is_resource($id)) {
  768.             return $this->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA);
  769.         }
  770.  
  771.         if ($this->options['portability'DB_PORTABILITY_LOWERCASE{
  772.             $case_func 'strtolower';
  773.         else {
  774.             $case_func 'strval';
  775.         }
  776.  
  777.         $count @mssql_num_fields($id);
  778.         $res   array();
  779.  
  780.         if ($mode{
  781.             $res['num_fields'$count;
  782.         }
  783.  
  784.         for ($i 0$i $count$i++{
  785.             if ($got_string{
  786.                 $flags $this->_mssql_field_flags($result,
  787.                         @mssql_field_name($id$i));
  788.                 if (DB::isError($flags)) {
  789.                     return $flags;
  790.                 }
  791.             else {
  792.                 $flags '';
  793.             }
  794.  
  795.             $res[$iarray(
  796.                 'table' => $got_string $case_func($result'',
  797.                 'name'  => $case_func(@mssql_field_name($id$i)),
  798.                 'type'  => @mssql_field_type($id$i),
  799.                 'len'   => @mssql_field_length($id$i),
  800.                 'flags' => $flags,
  801.             );
  802.             if ($mode DB_TABLEINFO_ORDER{
  803.                 $res['order'][$res[$i]['name']] $i;
  804.             }
  805.             if ($mode DB_TABLEINFO_ORDERTABLE{
  806.                 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] $i;
  807.             }
  808.         }
  809.  
  810.         // free the result only if we were called on a table
  811.         if ($got_string{
  812.             @mssql_free_result($id);
  813.         }
  814.         return $res;
  815.     }
  816.  
  817.     // }}}
  818.     // {{{ _mssql_field_flags()
  819.  
  820.     /**
  821.      * Get a column's flags
  822.      *
  823.      * Supports "not_null", "primary_key",
  824.      * "auto_increment" (mssql identity), "timestamp" (mssql timestamp),
  825.      * "unique_key" (mssql unique index, unique check or primary_key) and
  826.      * "multiple_key" (multikey index)
  827.      *
  828.      * mssql timestamp is NOT similar to the mysql timestamp so this is maybe
  829.      * not useful at all - is the behaviour of mysql_field_flags that primary
  830.      * keys are alway unique? is the interpretation of multiple_key correct?
  831.      *
  832.      * @param string $table   the table name
  833.      * @param string $column  the field name
  834.      *
  835.      * @return string  the flags
  836.      *
  837.      * @access private
  838.      * @author Joern Barthel <j_barthel@web.de>
  839.      */
  840.     function _mssql_field_flags($table$column)
  841.     {
  842.         static $tableName null;
  843.         static $flags array();
  844.  
  845.         if ($table != $tableName{
  846.  
  847.             $flags array();
  848.             $tableName $table;
  849.  
  850.             // get unique and primary keys
  851.             $res $this->getAll("EXEC SP_HELPINDEX $table"DB_FETCHMODE_ASSOC);
  852.             if (DB::isError($res)) {
  853.                 return $res;
  854.             }
  855.  
  856.             foreach ($res as $val{
  857.                 $keys explode(', '$val['index_keys']);
  858.  
  859.                 if (sizeof($keys1{
  860.                     foreach ($keys as $key{
  861.                         $this->_add_flag($flags[$key]'multiple_key');
  862.                     }
  863.                 }
  864.  
  865.                 if (strpos($val['index_description']'primary key')) {
  866.                     foreach ($keys as $key{
  867.                         $this->_add_flag($flags[$key]'primary_key');
  868.                     }
  869.                 elseif (strpos($val['index_description']'unique')) {
  870.                     foreach ($keys as $key{
  871.                         $this->_add_flag($flags[$key]'unique_key');
  872.                     }
  873.                 }
  874.             }
  875.  
  876.             // get auto_increment, not_null and timestamp
  877.             $res $this->getAll("EXEC SP_COLUMNS $table"DB_FETCHMODE_ASSOC);
  878.             if (DB::isError($res)) {
  879.                 return $res;
  880.             }
  881.  
  882.             foreach ($res as $val{
  883.                 $val array_change_key_case($valCASE_LOWER);
  884.                 if ($val['nullable'== '0'{
  885.                     $this->_add_flag($flags[$val['column_name']]'not_null');
  886.                 }
  887.                 if (strpos($val['type_name']'identity')) {
  888.                     $this->_add_flag($flags[$val['column_name']]'auto_increment');
  889.                 }
  890.                 if (strpos($val['type_name']'timestamp')) {
  891.                     $this->_add_flag($flags[$val['column_name']]'timestamp');
  892.                 }
  893.             }
  894.         }
  895.  
  896.         if (array_key_exists($column$flags)) {
  897.             return(implode(' '$flags[$column]));
  898.         }
  899.         return '';
  900.     }
  901.  
  902.     // }}}
  903.     // {{{ _add_flag()
  904.  
  905.     /**
  906.      * Adds a string to the flags array if the flag is not yet in there
  907.      * - if there is no flag present the array is created
  908.      *
  909.      * @param array  &$array  the reference to the flag-array
  910.      * @param string $value   the flag value
  911.      *
  912.      * @return void 
  913.      *
  914.      * @access private
  915.      * @author Joern Barthel <j_barthel@web.de>
  916.      */
  917.     function _add_flag(&$array$value)
  918.     {
  919.         if (!is_array($array)) {
  920.             $array array($value);
  921.         elseif (!in_array($value$array)) {
  922.             array_push($array$value);
  923.         }
  924.     }
  925.  
  926.     // }}}
  927.     // {{{ getSpecialQuery()
  928.  
  929.     /**
  930.      * Obtains the query string needed for listing a given type of objects
  931.      *
  932.      * @param string $type  the kind of objects you want to retrieve
  933.      *
  934.      * @return string  the SQL query string or null if the driver doesn't
  935.      *                   support the object type requested
  936.      *
  937.      * @access protected
  938.      * @see DB_common::getListOf()
  939.      */
  940.     function getSpecialQuery($type)
  941.     {
  942.         switch ($type{
  943.             case 'tables':
  944.                 return "SELECT name FROM sysobjects WHERE type = 'U'"
  945.                        . ' ORDER BY name';
  946.             case 'views':
  947.                 return "SELECT name FROM sysobjects WHERE type = 'V'";
  948.             default:
  949.                 return null;
  950.         }
  951.     }
  952.  
  953.     // }}}
  954. }
  955.  
  956. /*
  957.  * Local variables:
  958.  * tab-width: 4
  959.  * c-basic-offset: 4
  960.  * End:
  961.  */
  962.  
  963. ?>

Documentation generated on Wed, 09 Feb 2011 09:02:03 +0700 by phpDocumentor 1.4.2