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

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 306603 2010-12-24 06:05:07Z aharvey $
  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 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.14
  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($result? mssql_free_result($result: false;
  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.     // {{{ escapeSimple()
  628.  
  629.     /**
  630.      * Escapes a string in a manner suitable for SQL Server.
  631.      *
  632.      * @param string $str  the string to be escaped
  633.      * @return string  the escaped string
  634.      *
  635.      * @see DB_common::quoteSmart()
  636.      * @since Method available since Release 1.6.0
  637.      */
  638.     function escapeSimple($str)
  639.     {
  640.         return str_replace(
  641.             array("'""\\\r\n""\\\n"),
  642.             array("''""\\\\\r\n\r\n""\\\\\n\n"),
  643.             $str
  644.         );
  645.     }
  646.  
  647.     // }}}
  648.     // {{{ quoteIdentifier()
  649.  
  650.     /**
  651.      * Quotes a string so it can be safely used as a table or column name
  652.      *
  653.      * @param string $str  identifier name to be quoted
  654.      *
  655.      * @return string  quoted identifier string
  656.      *
  657.      * @see DB_common::quoteIdentifier()
  658.      * @since Method available since Release 1.6.0
  659.      */
  660.     function quoteIdentifier($str)
  661.     {
  662.         return '[' str_replace(']'']]'$str']';
  663.     }
  664.  
  665.     // }}}
  666.     // {{{ mssqlRaiseError()
  667.  
  668.     /**
  669.      * Produces a DB_Error object regarding the current problem
  670.      *
  671.      * @param int $errno  if the error is being manually raised pass a
  672.      *                      DB_ERROR* constant here.  If this isn't passed
  673.      *                      the error information gathered from the DBMS.
  674.      *
  675.      * @return object  the DB_Error object
  676.      *
  677.      * @see DB_common::raiseError(),
  678.      *       DB_mssql::errorNative(), DB_mssql::errorCode()
  679.      */
  680.     function mssqlRaiseError($code = null)
  681.     {
  682.         $message @mssql_get_last_message();
  683.         if (!$code{
  684.             $code $this->errorNative();
  685.         }
  686.         return $this->raiseError($this->errorCode($code$message),
  687.                                  nullnullnull"$code - $message");
  688.     }
  689.  
  690.     // }}}
  691.     // {{{ errorNative()
  692.  
  693.     /**
  694.      * Gets the DBMS' native error code produced by the last query
  695.      *
  696.      * @return int  the DBMS' error code
  697.      */
  698.     function errorNative()
  699.     {
  700.         $res @mssql_query('select @@ERROR as ErrorCode'$this->connection);
  701.         if (!$res{
  702.             return DB_ERROR;
  703.         }
  704.         $row @mssql_fetch_row($res);
  705.         return $row[0];
  706.     }
  707.  
  708.     // }}}
  709.     // {{{ errorCode()
  710.  
  711.     /**
  712.      * Determines PEAR::DB error code from mssql's native codes.
  713.      *
  714.      * If <var>$nativecode</var> isn't known yet, it will be looked up.
  715.      *
  716.      * @param  mixed  $nativecode  mssql error code, if known
  717.      * @return integer  an error number from a DB error constant
  718.      * @see errorNative()
  719.      */
  720.     function errorCode($nativecode = null$msg '')
  721.     {
  722.         if (!$nativecode{
  723.             $nativecode $this->errorNative();
  724.         }
  725.         if (isset($this->errorcode_map[$nativecode])) {
  726.             if ($nativecode == 3701
  727.                 && preg_match('/Cannot drop the index/i'$msg))
  728.             {
  729.                 return DB_ERROR_NOT_FOUND;
  730.             }
  731.             return $this->errorcode_map[$nativecode];
  732.         else {
  733.             return DB_ERROR;
  734.         }
  735.     }
  736.  
  737.     // }}}
  738.     // {{{ tableInfo()
  739.  
  740.     /**
  741.      * Returns information about a table or a result set
  742.      *
  743.      * NOTE: only supports 'table' and 'flags' if <var>$result</var>
  744.      * is a table name.
  745.      *
  746.      * @param object|string $result  DB_result object from a query or a
  747.      *                                  string containing the name of a table.
  748.      *                                  While this also accepts a query result
  749.      *                                  resource identifier, this behavior is
  750.      *                                  deprecated.
  751.      * @param int            $mode    a valid tableInfo mode
  752.      *
  753.      * @return array  an associative array with the information requested.
  754.      *                  A DB_Error object on failure.
  755.      *
  756.      * @see DB_common::tableInfo()
  757.      */
  758.     function tableInfo($result$mode = null)
  759.     {
  760.         if (is_string($result)) {
  761.             /*
  762.              * Probably received a table name.
  763.              * Create a result resource identifier.
  764.              */
  765.             if (!@mssql_select_db($this->_db$this->connection)) {
  766.                 return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  767.             }
  768.             $id @mssql_query("SELECT * FROM $result WHERE 1=0",
  769.                                $this->connection);
  770.             $got_string = true;
  771.         elseif (isset($result->result)) {
  772.             /*
  773.              * Probably received a result object.
  774.              * Extract the result resource identifier.
  775.              */
  776.             $id $result->result;
  777.             $got_string = false;
  778.         else {
  779.             /*
  780.              * Probably received a result resource identifier.
  781.              * Copy it.
  782.              * Deprecated.  Here for compatibility only.
  783.              */
  784.             $id $result;
  785.             $got_string = false;
  786.         }
  787.  
  788.         if (!is_resource($id)) {
  789.             return $this->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA);
  790.         }
  791.  
  792.         if ($this->options['portability'DB_PORTABILITY_LOWERCASE{
  793.             $case_func 'strtolower';
  794.         else {
  795.             $case_func 'strval';
  796.         }
  797.  
  798.         $count @mssql_num_fields($id);
  799.         $res   = array();
  800.  
  801.         if ($mode{
  802.             $res['num_fields'$count;
  803.         }
  804.  
  805.         for ($i = 0; $i $count$i++{
  806.             if ($got_string{
  807.                 $flags $this->_mssql_field_flags($result,
  808.                         @mssql_field_name($id$i));
  809.                 if (DB::isError($flags)) {
  810.                     return $flags;
  811.                 }
  812.             else {
  813.                 $flags '';
  814.             }
  815.  
  816.             $res[$i= array(
  817.                 'table' => $got_string $case_func($result'',
  818.                 'name'  => $case_func(@mssql_field_name($id$i)),
  819.                 'type'  => @mssql_field_type($id$i),
  820.                 'len'   => @mssql_field_length($id$i),
  821.                 'flags' => $flags,
  822.             );
  823.             if ($mode DB_TABLEINFO_ORDER{
  824.                 $res['order'][$res[$i]['name']] $i;
  825.             }
  826.             if ($mode DB_TABLEINFO_ORDERTABLE{
  827.                 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] $i;
  828.             }
  829.         }
  830.  
  831.         // free the result only if we were called on a table
  832.         if ($got_string{
  833.             @mssql_free_result($id);
  834.         }
  835.         return $res;
  836.     }
  837.  
  838.     // }}}
  839.     // {{{ _mssql_field_flags()
  840.  
  841.     /**
  842.      * Get a column's flags
  843.      *
  844.      * Supports "not_null", "primary_key",
  845.      * "auto_increment" (mssql identity), "timestamp" (mssql timestamp),
  846.      * "unique_key" (mssql unique index, unique check or primary_key) and
  847.      * "multiple_key" (multikey index)
  848.      *
  849.      * mssql timestamp is NOT similar to the mysql timestamp so this is maybe
  850.      * not useful at all - is the behaviour of mysql_field_flags that primary
  851.      * keys are alway unique? is the interpretation of multiple_key correct?
  852.      *
  853.      * @param string $table   the table name
  854.      * @param string $column  the field name
  855.      *
  856.      * @return string  the flags
  857.      *
  858.      * @access private
  859.      * @author Joern Barthel <j_barthel@web.de>
  860.      */
  861.     function _mssql_field_flags($table$column)
  862.     {
  863.         static $tableName = null;
  864.         static $flags = array();
  865.  
  866.         if ($table != $tableName{
  867.  
  868.             $flags = array();
  869.             $tableName $table;
  870.  
  871.             // get unique and primary keys
  872.             $res $this->getAll("EXEC SP_HELPINDEX $table"DB_FETCHMODE_ASSOC);
  873.             if (DB::isError($res)) {
  874.                 return $res;
  875.             }
  876.  
  877.             foreach ($res as $val{
  878.                 $keys explode(', '$val['index_keys']);
  879.  
  880.                 if (sizeof($keys> 1{
  881.                     foreach ($keys as $key{
  882.                         $this->_add_flag($flags[$key]'multiple_key');
  883.                     }
  884.                 }
  885.  
  886.                 if (strpos($val['index_description']'primary key')) {
  887.                     foreach ($keys as $key{
  888.                         $this->_add_flag($flags[$key]'primary_key');
  889.                     }
  890.                 elseif (strpos($val['index_description']'unique')) {
  891.                     foreach ($keys as $key{
  892.                         $this->_add_flag($flags[$key]'unique_key');
  893.                     }
  894.                 }
  895.             }
  896.  
  897.             // get auto_increment, not_null and timestamp
  898.             $res $this->getAll("EXEC SP_COLUMNS $table"DB_FETCHMODE_ASSOC);
  899.             if (DB::isError($res)) {
  900.                 return $res;
  901.             }
  902.  
  903.             foreach ($res as $val{
  904.                 $val array_change_key_case($valCASE_LOWER);
  905.                 if ($val['nullable'== '0'{
  906.                     $this->_add_flag($flags[$val['column_name']]'not_null');
  907.                 }
  908.                 if (strpos($val['type_name']'identity')) {
  909.                     $this->_add_flag($flags[$val['column_name']]'auto_increment');
  910.                 }
  911.                 if (strpos($val['type_name']'timestamp')) {
  912.                     $this->_add_flag($flags[$val['column_name']]'timestamp');
  913.                 }
  914.             }
  915.         }
  916.  
  917.         if (array_key_exists($column$flags)) {
  918.             return(implode(' '$flags[$column]));
  919.         }
  920.         return '';
  921.     }
  922.  
  923.     // }}}
  924.     // {{{ _add_flag()
  925.  
  926.     /**
  927.      * Adds a string to the flags array if the flag is not yet in there
  928.      * - if there is no flag present the array is created
  929.      *
  930.      * @param array  &$array  the reference to the flag-array
  931.      * @param string $value   the flag value
  932.      *
  933.      * @return void 
  934.      *
  935.      * @access private
  936.      * @author Joern Barthel <j_barthel@web.de>
  937.      */
  938.     function _add_flag(&$array$value)
  939.     {
  940.         if (!is_array($array)) {
  941.             $array = array($value);
  942.         elseif (!in_array($value$array)) {
  943.             array_push($array$value);
  944.         }
  945.     }
  946.  
  947.     // }}}
  948.     // {{{ getSpecialQuery()
  949.  
  950.     /**
  951.      * Obtains the query string needed for listing a given type of objects
  952.      *
  953.      * @param string $type  the kind of objects you want to retrieve
  954.      *
  955.      * @return string  the SQL query string or null if the driver doesn't
  956.      *                   support the object type requested
  957.      *
  958.      * @access protected
  959.      * @see DB_common::getListOf()
  960.      */
  961.     function getSpecialQuery($type)
  962.     {
  963.         switch ($type{
  964.             case 'tables':
  965.                 return "SELECT name FROM sysobjects WHERE type = 'U'"
  966.                        . ' ORDER BY name';
  967.             case 'views':
  968.                 return "SELECT name FROM sysobjects WHERE type = 'V'";
  969.             default:
  970.                 return null;
  971.         }
  972.     }
  973.  
  974.     // }}}
  975. }
  976.  
  977. /*
  978.  * Local variables:
  979.  * tab-width: 4
  980.  * c-basic-offset: 4
  981.  * End:
  982.  */
  983.  
  984. ?>

Documentation generated on Sat, 27 Aug 2011 14:00:12 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.