home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / MDB2 / Driver / mssql.php < prev    next >
Encoding:
PHP Script  |  2008-07-02  |  32.7 KB  |  963 lines

  1. <?php
  2. // vim: set et ts=4 sw=4 fdm=marker:
  3. // +----------------------------------------------------------------------+
  4. // | PHP versions 4 and 5                                                 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
  7. // | Stig. S. Bakken, Lukas Smith, Frank M. Kromann                       |
  8. // | All rights reserved.                                                 |
  9. // +----------------------------------------------------------------------+
  10. // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
  11. // | API as well as database abstraction for PHP applications.            |
  12. // | This LICENSE is in the BSD license style.                            |
  13. // |                                                                      |
  14. // | Redistribution and use in source and binary forms, with or without   |
  15. // | modification, are permitted provided that the following conditions   |
  16. // | are met:                                                             |
  17. // |                                                                      |
  18. // | Redistributions of source code must retain the above copyright       |
  19. // | notice, this list of conditions and the following disclaimer.        |
  20. // |                                                                      |
  21. // | Redistributions in binary form must reproduce the above copyright    |
  22. // | notice, this list of conditions and the following disclaimer in the  |
  23. // | documentation and/or other materials provided with the distribution. |
  24. // |                                                                      |
  25. // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
  26. // | Lukas Smith nor the names of his contributors may be used to endorse |
  27. // | or promote products derived from this software without specific prior|
  28. // | written permission.                                                  |
  29. // |                                                                      |
  30. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
  31. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
  32. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
  33. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
  34. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
  35. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  36. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  37. // |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
  38. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
  39. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  40. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
  41. // | POSSIBILITY OF SUCH DAMAGE.                                          |
  42. // +----------------------------------------------------------------------+
  43. // | Author: Frank M. Kromann <frank@kromann.info>                        |
  44. // +----------------------------------------------------------------------+
  45. //
  46. // $Id: mssql.php,v 1.155 2007/03/02 19:42:30 quipo Exp $
  47. //
  48. // {{{ Class MDB2_Driver_mssql
  49. /**
  50.  * MDB2 MSSQL Server driver
  51.  *
  52.  * @package MDB2
  53.  * @category Database
  54.  * @author  Frank M. Kromann <frank@kromann.info>
  55.  */
  56. class MDB2_Driver_mssql extends MDB2_Driver_Common
  57. {
  58.     // {{{ properties
  59.     var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => false);
  60.  
  61.     var $identifier_quoting = array('start' => '[', 'end' => ']', 'escape' => ']');
  62.     // }}}
  63.     // {{{ constructor
  64.  
  65.     /**
  66.      * Constructor
  67.      */
  68.     function __construct()
  69.     {
  70.         parent::__construct();
  71.  
  72.         $this->phptype = 'mssql';
  73.         $this->dbsyntax = 'mssql';
  74.  
  75.         $this->supported['sequences'] = 'emulated';
  76.         $this->supported['indexes'] = true;
  77.         $this->supported['affected_rows'] = true;
  78.         $this->supported['transactions'] = true;
  79.         $this->supported['savepoints'] = false;
  80.         $this->supported['summary_functions'] = true;
  81.         $this->supported['order_by_text'] = true;
  82.         $this->supported['current_id'] = 'emulated';
  83.         $this->supported['limit_queries'] = 'emulated';
  84.         $this->supported['LOBs'] = true;
  85.         $this->supported['replace'] = 'emulated';
  86.         $this->supported['sub_selects'] = true;
  87.         $this->supported['auto_increment'] = true;
  88.         $this->supported['primary_key'] = true;
  89.         $this->supported['result_introspection'] = true;
  90.         $this->supported['prepared_statements'] = 'emulated';
  91.         $this->supported['pattern_escaping'] = true;
  92.  
  93.         $this->options['database_device'] = false;
  94.         $this->options['database_size'] = false;
  95.     }
  96.  
  97.     // }}}
  98.     // {{{ errorInfo()
  99.  
  100.     /**
  101.      * This method is used to collect information about an error
  102.      *
  103.      * @param integer $error
  104.      * @return array
  105.      * @access public
  106.      */
  107.     function errorInfo($error = null)
  108.     {
  109.         $native_code = null;
  110.         if ($this->connection) {
  111.             $result = @mssql_query('select @@ERROR as ErrorCode', $this->connection);
  112.             if ($result) {
  113.                 $native_code = @mssql_result($result, 0, 0);
  114.                 @mssql_free_result($result);
  115.             }
  116.         }
  117.         $native_msg = @mssql_get_last_message();
  118.         if (is_null($error)) {
  119.             static $ecode_map;
  120.             if (empty($ecode_map)) {
  121.                 $ecode_map = array(
  122.                     102   => MDB2_ERROR_SYNTAX,
  123.                     110   => MDB2_ERROR_VALUE_COUNT_ON_ROW,
  124.                     155   => MDB2_ERROR_NOSUCHFIELD,
  125.                     156   => MDB2_ERROR_SYNTAX,
  126.                     170   => MDB2_ERROR_SYNTAX,
  127.                     207   => MDB2_ERROR_NOSUCHFIELD,
  128.                     208   => MDB2_ERROR_NOSUCHTABLE,
  129.                     245   => MDB2_ERROR_INVALID_NUMBER,
  130.                     319   => MDB2_ERROR_SYNTAX,
  131.                     321   => MDB2_ERROR_NOSUCHFIELD,
  132.                     325   => MDB2_ERROR_SYNTAX,
  133.                     336   => MDB2_ERROR_SYNTAX,
  134.                     515   => MDB2_ERROR_CONSTRAINT_NOT_NULL,
  135.                     547   => MDB2_ERROR_CONSTRAINT,
  136.                     1018  => MDB2_ERROR_SYNTAX,
  137.                     1035  => MDB2_ERROR_SYNTAX,
  138.                     1913  => MDB2_ERROR_ALREADY_EXISTS,
  139.                     2209  => MDB2_ERROR_SYNTAX,
  140.                     2223  => MDB2_ERROR_SYNTAX,
  141.                     2248  => MDB2_ERROR_SYNTAX,
  142.                     2256  => MDB2_ERROR_SYNTAX,
  143.                     2257  => MDB2_ERROR_SYNTAX,
  144.                     2627  => MDB2_ERROR_CONSTRAINT,
  145.                     2714  => MDB2_ERROR_ALREADY_EXISTS,
  146.                     3607  => MDB2_ERROR_DIVZERO,
  147.                     3701  => MDB2_ERROR_NOSUCHTABLE,
  148.                     7630  => MDB2_ERROR_SYNTAX,
  149.                     8134  => MDB2_ERROR_DIVZERO,
  150.                     9303  => MDB2_ERROR_SYNTAX,
  151.                     9317  => MDB2_ERROR_SYNTAX,
  152.                     9318  => MDB2_ERROR_SYNTAX,
  153.                     9331  => MDB2_ERROR_SYNTAX,
  154.                     9332  => MDB2_ERROR_SYNTAX,
  155.                     15253 => MDB2_ERROR_SYNTAX,
  156.                 );
  157.             }
  158.             if (isset($ecode_map[$native_code])) {
  159.                 if ($native_code == 3701
  160.                     && preg_match('/Cannot drop the index/i', $native_msg)
  161.                 ) {
  162.                    $error = MDB2_ERROR_NOT_FOUND;
  163.                 } else {
  164.                     $error = $ecode_map[$native_code];
  165.                 }
  166.             }
  167.         }
  168.         return array($error, $native_code, $native_msg);
  169.     }
  170.  
  171.     // }}}
  172.     // {{{ function escapePattern($text)
  173.  
  174.     /**
  175.      * Quotes pattern (% and _) characters in a string)
  176.      *
  177.      * @param   string  the input string to quote
  178.      *
  179.      * @return  string  quoted string
  180.      *
  181.      * @access  public
  182.      */
  183.     function escapePattern($text)
  184.     {
  185.         $text = str_replace("[", "[ [ ]", $text);
  186.         foreach ($this->wildcards as $wildcard) {
  187.             $text = str_replace($wildcard, '[' . $wildcard . ']', $text);
  188.         }
  189.         return $text;
  190.     }
  191.  
  192.     // }}}
  193.     // {{{ beginTransaction()
  194.  
  195.     /**
  196.      * Start a transaction or set a savepoint.
  197.      *
  198.      * @param   string  name of a savepoint to set
  199.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  200.      *
  201.      * @access  public
  202.      */
  203.     function beginTransaction($savepoint = null)
  204.     {
  205.         $this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  206.         if (!is_null($savepoint)) {
  207.             if (!$this->in_transaction) {
  208.                 return $this->raiseError(MDB2_ERROR_INVALID, null, null,
  209.                     'savepoint cannot be released when changes are auto committed', __FUNCTION__);
  210.             }
  211.             $query = 'SAVE TRANSACTION '.$savepoint;
  212.             return $this->_doQuery($query, true);
  213.         } elseif ($this->in_transaction) {
  214.             return MDB2_OK;  //nothing to do
  215.         }
  216.         if (!$this->destructor_registered && $this->opened_persistent) {
  217.             $this->destructor_registered = true;
  218.             register_shutdown_function('MDB2_closeOpenTransactions');
  219.         }
  220.         $result =& $this->_doQuery('BEGIN TRANSACTION', true);
  221.         if (PEAR::isError($result)) {
  222.             return $result;
  223.         }
  224.         $this->in_transaction = true;
  225.         return MDB2_OK;
  226.     }
  227.  
  228.     // }}}
  229.     // {{{ commit()
  230.  
  231.     /**
  232.      * Commit the database changes done during a transaction that is in
  233.      * progress or release a savepoint. This function may only be called when
  234.      * auto-committing is disabled, otherwise it will fail. Therefore, a new
  235.      * transaction is implicitly started after committing the pending changes.
  236.      *
  237.      * @param   string  name of a savepoint to release
  238.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  239.      *
  240.      * @access  public
  241.      */
  242.     function commit($savepoint = null)
  243.     {
  244.         $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  245.         if (!$this->in_transaction) {
  246.             return $this->raiseError(MDB2_ERROR_INVALID, null, null,
  247.                 'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__);
  248.         }
  249.         if (!is_null($savepoint)) {
  250.             return MDB2_OK;
  251.         }
  252.  
  253.         $result =& $this->_doQuery('COMMIT TRANSACTION', true);
  254.         if (PEAR::isError($result)) {
  255.             return $result;
  256.         }
  257.         $this->in_transaction = false;
  258.         return MDB2_OK;
  259.     }
  260.  
  261.     // }}}
  262.     // {{{ rollback()
  263.  
  264.     /**
  265.      * Cancel any database changes done during a transaction or since a specific
  266.      * savepoint that is in progress. This function may only be called when
  267.      * auto-committing is disabled, otherwise it will fail. Therefore, a new
  268.      * transaction is implicitly started after canceling the pending changes.
  269.      *
  270.      * @param   string  name of a savepoint to rollback to
  271.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  272.      *
  273.      * @access  public
  274.      */
  275.     function rollback($savepoint = null)
  276.     {
  277.         $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  278.         if (!$this->in_transaction) {
  279.             return $this->raiseError(MDB2_ERROR_INVALID, null, null,
  280.                 'rollback cannot be done changes are auto committed', __FUNCTION__);
  281.         }
  282.         if (!is_null($savepoint)) {
  283.             $query = 'ROLLBACK TRANSACTION '.$savepoint;
  284.             return $this->_doQuery($query, true);
  285.         }
  286.  
  287.         $result =& $this->_doQuery('ROLLBACK TRANSACTION', true);
  288.         if (PEAR::isError($result)) {
  289.             return $result;
  290.         }
  291.         $this->in_transaction = false;
  292.         return MDB2_OK;
  293.     }
  294.  
  295.     // }}}
  296.     // {{{ connect()
  297.  
  298.     /**
  299.      * Connect to the database
  300.      *
  301.      * @return true on success, MDB2 Error Object on failure
  302.      */
  303.     function connect()
  304.     {
  305.         if (is_resource($this->connection)) {
  306.             if (count(array_diff($this->connected_dsn, $this->dsn)) == 0
  307.                 && $this->opened_persistent == $this->options['persistent']
  308.             ) {
  309.                 return MDB2_OK;
  310.             }
  311.             $this->disconnect(false);
  312.         }
  313.  
  314.         if (!PEAR::loadExtension($this->phptype)) {
  315.             return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  316.                 'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__);
  317.         }
  318.  
  319.         $params = array(
  320.             $this->dsn['hostspec'] ? $this->dsn['hostspec'] : 'localhost',
  321.             $this->dsn['username'] ? $this->dsn['username'] : null,
  322.             $this->dsn['password'] ? $this->dsn['password'] : null,
  323.         );
  324.         if ($this->dsn['port']) {
  325.             $params[0].= ((substr(PHP_OS, 0, 3) == 'WIN') ? ',' : ':').$this->dsn['port'];
  326.         }
  327.  
  328.         $connect_function = $this->options['persistent'] ? 'mssql_pconnect' : 'mssql_connect';
  329.  
  330.         $connection = @call_user_func_array($connect_function, $params);
  331.         if ($connection <= 0) {
  332.             return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
  333.                 'unable to establish a connection', __FUNCTION__, __FUNCTION__);
  334.         }
  335.  
  336.         if (!empty($this->dsn['charset'])) {
  337.             $result = $this->setCharset($this->dsn['charset'], $connection);
  338.             if (PEAR::isError($result)) {
  339.                 return $result;
  340.             }
  341.         }
  342.  
  343.        if ((bool)ini_get('mssql.datetimeconvert')) {
  344.            @ini_set('mssql.datetimeconvert', '0');
  345.        }
  346.  
  347.        if (empty($this->dsn['disable_iso_date'])) {
  348.            @mssql_query('SET DATEFORMAT ymd', $connection);
  349.        }
  350.  
  351.         $this->connection = $connection;
  352.         $this->connected_dsn = $this->dsn;
  353.         $this->connected_database_name = '';
  354.         $this->opened_persistent = $this->options['persistent'];
  355.         $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
  356.  
  357.         if ($this->database_name) {
  358.             if ($this->database_name != $this->connected_database_name) {
  359.                 if (!@mssql_select_db($this->database_name, $connection)) {
  360.                     $err = $this->raiseError(null, null, null,
  361.                         'Could not select the database: '.$this->database_name, __FUNCTION__);
  362.                     return $err;
  363.                 }
  364.                 $this->connected_database_name = $this->database_name;
  365.             }
  366.         }
  367.  
  368.         return MDB2_OK;
  369.     }
  370.  
  371.     // }}}
  372.     // {{{ disconnect()
  373.  
  374.     /**
  375.      * Log out and disconnect from the database.
  376.      *
  377.      * @param  boolean $force if the disconnect should be forced even if the
  378.      *                        connection is opened persistently
  379.      * @return mixed true on success, false if not connected and error
  380.      *                object on error
  381.      * @access public
  382.      */
  383.     function disconnect($force = true)
  384.     {
  385.         if (is_resource($this->connection)) {
  386.             if ($this->in_transaction) {
  387.                 $dsn = $this->dsn;
  388.                 $database_name = $this->database_name;
  389.                 $persistent = $this->options['persistent'];
  390.                 $this->dsn = $this->connected_dsn;
  391.                 $this->database_name = $this->connected_database_name;
  392.                 $this->options['persistent'] = $this->opened_persistent;
  393.                 $this->rollback();
  394.                 $this->dsn = $dsn;
  395.                 $this->database_name = $database_name;
  396.                 $this->options['persistent'] = $persistent;
  397.             }
  398.  
  399.             if (!$this->opened_persistent || $force) {
  400.                 @mssql_close($this->connection);
  401.             }
  402.         }
  403.         return parent::disconnect($force);
  404.     }
  405.  
  406.     // }}}
  407.     // {{{ _doQuery()
  408.  
  409.     /**
  410.      * Execute a query
  411.      * @param string $query  query
  412.      * @param boolean $is_manip  if the query is a manipulation query
  413.      * @param resource $connection
  414.      * @param string $database_name
  415.      * @return result or error object
  416.      * @access protected
  417.      */
  418.     function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
  419.     {
  420.         $this->last_query = $query;
  421.         $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
  422.         if ($result) {
  423.             if (PEAR::isError($result)) {
  424.                 return $result;
  425.             }
  426.             $query = $result;
  427.         }
  428.         if ($this->options['disable_query']) {
  429.             $result = $is_manip ? 0 : null;
  430.             return $result;
  431.         }
  432.  
  433.         if (is_null($connection)) {
  434.             $connection = $this->getConnection();
  435.             if (PEAR::isError($connection)) {
  436.                 return $connection;
  437.             }
  438.         }
  439.         if (is_null($database_name)) {
  440.             $database_name = $this->database_name;
  441.         }
  442.  
  443.         if ($database_name) {
  444.             if ($database_name != $this->connected_database_name) {
  445.                 if (!@mssql_select_db($database_name, $connection)) {
  446.                     $err = $this->raiseError(null, null, null,
  447.                         'Could not select the database: '.$database_name, __FUNCTION__);
  448.                     return $err;
  449.                 }
  450.                 $this->connected_database_name = $database_name;
  451.             }
  452.         }
  453.  
  454.         $result = @mssql_query($query, $connection);
  455.         if (!$result) {
  456.             $err =& $this->raiseError(null, null, null,
  457.                 'Could not execute statement', __FUNCTION__);
  458.             return $err;
  459.         }
  460.  
  461.         $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
  462.         return $result;
  463.     }
  464.  
  465.     // }}}
  466.     // {{{ _affectedRows()
  467.  
  468.     /**
  469.      * Returns the number of rows affected
  470.      *
  471.      * @param resource $result
  472.      * @param resource $connection
  473.      * @return mixed MDB2 Error Object or the number of rows affected
  474.      * @access private
  475.      */
  476.     function _affectedRows($connection, $result = null)
  477.     {
  478.         if (is_null($connection)) {
  479.             $connection = $this->getConnection();
  480.             if (PEAR::isError($connection)) {
  481.                 return $connection;
  482.             }
  483.         }
  484.         return @mssql_rows_affected($connection);
  485.     }
  486.  
  487.     // }}}
  488.     // {{{ _modifyQuery()
  489.  
  490.     /**
  491.      * Changes a query string for various DBMS specific reasons
  492.      *
  493.      * @param string $query  query to modify
  494.      * @param boolean $is_manip  if it is a DML query
  495.      * @param integer $limit  limit the number of rows
  496.      * @param integer $offset  start reading from given offset
  497.      * @return string modified query
  498.      * @access protected
  499.      */
  500.     function _modifyQuery($query, $is_manip, $limit, $offset)
  501.     {
  502.         if ($limit > 0) {
  503.             $fetch = $offset + $limit;
  504.             if (!$is_manip) {
  505.                 return preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',
  506.                     "\\1SELECT\\2 TOP $fetch", $query);
  507.             }
  508.         }
  509.         return $query;
  510.     }
  511.     // }}}
  512.     // {{{ getServerVersion()
  513.  
  514.     /**
  515.      * return version information about the server
  516.      *
  517.      * @param bool   $native  determines if the raw version string should be returned
  518.      * @return mixed array/string with version information or MDB2 error object
  519.      * @access public
  520.      */
  521.     function getServerVersion($native = false)
  522.     {
  523.         if ($this->connected_server_info) {
  524.             $server_info = $this->connected_server_info;
  525.         } else {
  526.             $query = 'SELECT @@VERSION';
  527.             $server_info = $this->queryOne($query, 'text');
  528.             if (PEAR::isError($server_info)) {
  529.                 return $server_info;
  530.             }
  531.         }
  532.         // cache server_info
  533.         $this->connected_server_info = $server_info;
  534.         if (!$native && !PEAR::isError($server_info)) {
  535.             if (preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $server_info, $tmp)) {
  536.                 $server_info = array(
  537.                     'major' => $tmp[1],
  538.                     'minor' => $tmp[2],
  539.                     'patch' => $tmp[3],
  540.                     'extra' => null,
  541.                     'native' => $server_info,
  542.                 );
  543.             } else {
  544.                 $server_info = array(
  545.                     'major' => null,
  546.                     'minor' => null,
  547.                     'patch' => null,
  548.                     'extra' => null,
  549.                     'native' => $server_info,
  550.                 );
  551.             }
  552.         }
  553.         return $server_info;
  554.     }
  555.  
  556.     // }}}
  557.     // {{{ _checkSequence
  558.     /**
  559.      * Checks if there's a sequence that exists.
  560.      *
  561.      * @param  string $seq_name    The sequence name to verify.
  562.      * @return bool   $tableExists The value if the table exists or not
  563.      * @access private
  564.      */
  565.     function _checkSequence($seq_name)
  566.     {
  567.         $query = "SELECT * FROM $seq_name";
  568.         $tableExists =& $this->_doQuery($query, true);
  569.         if (PEAR::isError($tableExists)) {
  570.             if ($tableExists->getCode() == MDB2_ERROR_NOSUCHTABLE) {
  571.                 return false;
  572.             }
  573.             //return $tableExists;
  574.             return false;
  575.         }
  576.         return mssql_result($tableExists, 0, 0);
  577.     }
  578.  
  579.     // }}}
  580.     // {{{ nextID()
  581.  
  582.     /**
  583.      * Returns the next free id of a sequence
  584.      *
  585.      * @param string $seq_name name of the sequence
  586.      * @param boolean $ondemand when true the sequence is
  587.      *                          automatic created, if it
  588.      *                          not exists
  589.      *
  590.      * @return mixed MDB2 Error Object or id
  591.      * @access public
  592.      */
  593.     function nextID($seq_name, $ondemand = true)
  594.     {
  595.         $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
  596.         $seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
  597.         $this->expectError(MDB2_ERROR_NOSUCHTABLE);
  598.         
  599.         $seq_val = $this->_checkSequence($sequence_name);
  600.  
  601.         if ($seq_val) {
  602.             $query = "SET IDENTITY_INSERT $sequence_name OFF ".
  603.                      "INSERT INTO $sequence_name ($seqcol_name) DEFAULT VALUES";
  604.         } else {
  605.             $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (0)";
  606.         }
  607.         $result =& $this->_doQuery($query, true);
  608.         $this->popExpect();
  609.         if (PEAR::isError($result)) {
  610.             if ($ondemand && !$this->_checkSequence($sequence_name)) {
  611.                 $this->loadModule('Manager', null, true);
  612.                 $result = $this->manager->createSequence($seq_name);
  613.                 if (PEAR::isError($result)) {
  614.                     return $this->raiseError($result, null, null,
  615.                         'on demand sequence '.$seq_name.' could not be created', __FUNCTION__);
  616.                 } else {
  617.                     return $this->nextID($seq_name, false);
  618.                 }
  619.             }
  620.             return $result;
  621.         }
  622.         $value = $this->lastInsertID($sequence_name);
  623.         if (is_numeric($value)) {
  624.             $query = "DELETE FROM $sequence_name WHERE $seqcol_name < $value";
  625.             $result =& $this->_doQuery($query, true);
  626.             if (PEAR::isError($result)) {
  627.                 $this->warnings[] = 'nextID: could not delete previous sequence table values from '.$seq_name;
  628.             }
  629.         }
  630.         return $value;
  631.     }
  632.     // }}}
  633.     // {{{ lastInsertID()
  634.     /**
  635.      * Returns the autoincrement ID if supported or $id or fetches the current
  636.      * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
  637.      *
  638.      * @param string $table name of the table into which a new row was inserted
  639.      * @param string $field name of the field into which a new row was inserted
  640.      * @return mixed MDB2 Error Object or id
  641.      * @access public
  642.      */
  643.     function lastInsertID($table = null, $field = null)
  644.     {
  645.         $server_info = $this->getServerVersion();
  646.         if (is_array($server_info) && !is_null($server_info['major'])
  647.            && $server_info['major'] >= 8
  648.         ) {
  649.             $query = "SELECT SCOPE_IDENTITY()";
  650.         } else {
  651.             $query = "SELECT @@IDENTITY";
  652.         }
  653.  
  654.         return $this->queryOne($query, 'integer');
  655.     }
  656.     // }}}
  657. }
  658. // }}}
  659. // {{{ Class MDB2_Result_mssql
  660.  
  661. /**
  662.  * MDB2 MSSQL Server result driver
  663.  *
  664.  * @package MDB2
  665.  * @category Database
  666.  * @author  Frank M. Kromann <frank@kromann.info>
  667.  */
  668. class MDB2_Result_mssql extends MDB2_Result_Common
  669. {
  670.     // {{{ _skipLimitOffset()
  671.  
  672.     /**
  673.      * Skip the first row of a result set.
  674.      *
  675.      * @param resource $result
  676.      * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
  677.      * @access protected
  678.      */
  679.     function _skipLimitOffset()
  680.     {
  681.         if ($this->limit) {
  682.             if ($this->rownum >= $this->limit) {
  683.                 return false;
  684.             }
  685.         }
  686.         if ($this->offset) {
  687.             while ($this->offset_count < $this->offset) {
  688.                 ++$this->offset_count;
  689.                 if (!is_array(@mssql_fetch_row($this->result))) {
  690.                     $this->offset_count = $this->limit;
  691.                     return false;
  692.                 }
  693.             }
  694.         }
  695.         return MDB2_OK;
  696.     }
  697.  
  698.     // }}}
  699.     // {{{ fetchRow()
  700.  
  701.     /**
  702.      * Fetch a row and insert the data into an existing array.
  703.      *
  704.      * @param int       $fetchmode  how the array data should be indexed
  705.      * @param int    $rownum    number of the row where the data can be found
  706.      * @return int data array on success, a MDB2 error on failure
  707.      * @access public
  708.      */
  709.     function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
  710.     {
  711.         if (!$this->_skipLimitOffset()) {
  712.             $null = null;
  713.             return $null;
  714.         }
  715.         if (!is_null($rownum)) {
  716.             $seek = $this->seek($rownum);
  717.             if (PEAR::isError($seek)) {
  718.                 return $seek;
  719.             }
  720.         }
  721.         if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
  722.             $fetchmode = $this->db->fetchmode;
  723.         }
  724.         if ($fetchmode & MDB2_FETCHMODE_ASSOC) {
  725.             $row = @mssql_fetch_assoc($this->result);
  726.             if (is_array($row)
  727.                 && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
  728.             ) {
  729.                 $row = array_change_key_case($row, $this->db->options['field_case']);
  730.             }
  731.         } else {
  732.             $row = @mssql_fetch_row($this->result);
  733.         }
  734.         if (!$row) {
  735.             if ($this->result === false) {
  736.                 $err =& $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  737.                     'resultset has already been freed', __FUNCTION__);
  738.                 return $err;
  739.             }
  740.             $null = null;
  741.             return $null;
  742.         }
  743.         $mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
  744.         if ($mode) {
  745.             $this->db->_fixResultArrayValues($row, $mode);
  746.         }
  747.         if (!empty($this->types)) {
  748.             $row = $this->db->datatype->convertResultRow($this->types, $row, false);
  749.         }
  750.         if (!empty($this->values)) {
  751.             $this->_assignBindColumns($row);
  752.         }
  753.         if ($fetchmode === MDB2_FETCHMODE_OBJECT) {
  754.             $object_class = $this->db->options['fetch_class'];
  755.             if ($object_class == 'stdClass') {
  756.                 $row = (object) $row;
  757.             } else {
  758.                 $row = &new $object_class($row);
  759.             }
  760.         }
  761.         ++$this->rownum;
  762.         return $row;
  763.     }
  764.  
  765.     // }}}
  766.     // {{{ _getColumnNames()
  767.  
  768.     /**
  769.      * Retrieve the names of columns returned by the DBMS in a query result.
  770.      *
  771.      * @return  mixed   Array variable that holds the names of columns as keys
  772.      *                  or an MDB2 error on failure.
  773.      *                  Some DBMS may not return any columns when the result set
  774.      *                  does not contain any rows.
  775.      * @access private
  776.      */
  777.     function _getColumnNames()
  778.     {
  779.         $columns = array();
  780.         $numcols = $this->numCols();
  781.         if (PEAR::isError($numcols)) {
  782.             return $numcols;
  783.         }
  784.         for ($column = 0; $column < $numcols; $column++) {
  785.             $column_name = @mssql_field_name($this->result, $column);
  786.             $columns[$column_name] = $column;
  787.         }
  788.         if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  789.             $columns = array_change_key_case($columns, $this->db->options['field_case']);
  790.         }
  791.         return $columns;
  792.     }
  793.  
  794.     // }}}
  795.     // {{{ numCols()
  796.  
  797.     /**
  798.      * Count the number of columns returned by the DBMS in a query result.
  799.      *
  800.      * @return mixed integer value with the number of columns, a MDB2 error
  801.      *      on failure
  802.      * @access public
  803.      */
  804.     function numCols()
  805.     {
  806.         $cols = @mssql_num_fields($this->result);
  807.         if (is_null($cols)) {
  808.             if ($this->result === false) {
  809.                 return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  810.                     'resultset has already been freed', __FUNCTION__);
  811.             } elseif (is_null($this->result)) {
  812.                 return count($this->types);
  813.             }
  814.             return $this->db->raiseError(null, null, null,
  815.                 'Could not get column count', __FUNCTION__);
  816.         }
  817.         return $cols;
  818.     }
  819.  
  820.     // }}}
  821.     // {{{ nextResult()
  822.  
  823.     /**
  824.      * Move the internal result pointer to the next available result
  825.      *
  826.      * @return true on success, false if there is no more result set or an error object on failure
  827.      * @access public
  828.      */
  829.     function nextResult()
  830.     {
  831.         if ($this->result === false) {
  832.             return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  833.                 'resultset has already been freed', __FUNCTION__);
  834.         } elseif (is_null($this->result)) {
  835.             return false;
  836.         }
  837.         return @mssql_next_result($this->result);
  838.     }
  839.  
  840.     // }}}
  841.     // {{{ free()
  842.  
  843.     /**
  844.      * Free the internal resources associated with $result.
  845.      *
  846.      * @return boolean true on success, false if $result is invalid
  847.      * @access public
  848.      */
  849.     function free()
  850.     {
  851.         if (is_resource($this->result) && $this->db->connection) {
  852.             $free = @mssql_free_result($this->result);
  853.             if ($free === false) {
  854.                 return $this->db->raiseError(null, null, null,
  855.                     'Could not free result', __FUNCTION__);
  856.             }
  857.         }
  858.         $this->result = false;
  859.         return MDB2_OK;
  860.     }
  861. }
  862.  
  863. /**
  864.  * MDB2 MSSQL Server buffered result driver
  865.  *
  866.  * @package MDB2
  867.  * @category Database
  868.  * @author  Frank M. Kromann <frank@kromann.info>
  869.  */
  870. class MDB2_BufferedResult_mssql extends MDB2_Result_mssql
  871. {
  872.     // }}}
  873.     // {{{ seek()
  874.  
  875.     /**
  876.      * Seek to a specific row in a result set
  877.      *
  878.      * @param int    $rownum    number of the row where the data can be found
  879.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  880.      * @access public
  881.      */
  882.     function seek($rownum = 0)
  883.     {
  884.         if ($this->rownum != ($rownum - 1) && !@mssql_data_seek($this->result, $rownum)) {
  885.             if ($this->result === false) {
  886.                 return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  887.                     'resultset has already been freed', __FUNCTION__);
  888.             } elseif (is_null($this->result)) {
  889.                 return MDB2_OK;
  890.             }
  891.             return $this->db->raiseError(MDB2_ERROR_INVALID, null, null,
  892.                 'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__);
  893.         }
  894.         $this->rownum = $rownum - 1;
  895.         return MDB2_OK;
  896.     }
  897.  
  898.     // {{{ valid()
  899.  
  900.     /**
  901.      * Check if the end of the result set has been reached
  902.      *
  903.      * @return mixed true or false on sucess, a MDB2 error on failure
  904.      * @access public
  905.      */
  906.     function valid()
  907.     {
  908.         $numrows = $this->numRows();
  909.         if (PEAR::isError($numrows)) {
  910.             return $numrows;
  911.         }
  912.         return $this->rownum < ($numrows - 1);
  913.     }
  914.  
  915.     // }}}
  916.     // {{{ numRows()
  917.  
  918.     /**
  919.      * Returns the number of rows in a result object
  920.      *
  921.      * @return mixed MDB2 Error Object or the number of rows
  922.      * @access public
  923.      */
  924.     function numRows()
  925.     {
  926.         $rows = @mssql_num_rows($this->result);
  927.         if (is_null($rows)) {
  928.             if ($this->result === false) {
  929.                 return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  930.                     'resultset has already been freed', __FUNCTION__);
  931.             } elseif (is_null($this->result)) {
  932.                 return 0;
  933.             }
  934.             return $this->db->raiseError(null, null, null,
  935.                 'Could not get row count', __FUNCTION__);
  936.         }
  937.         if ($this->limit) {
  938.             $rows -= $this->limit;
  939.             if ($rows < 0) {
  940.                 $rows = 0;
  941.             }
  942.         }
  943.         return $rows;
  944.     }
  945. }
  946. // }}}
  947. // {{{ MDB2_Statement_mssql
  948.  
  949. /**
  950.  * MDB2 MSSQL Server statement driver
  951.  *
  952.  * @package MDB2
  953.  * @category Database
  954.  * @author  Frank M. Kromann <frank@kromann.info>
  955.  */
  956. class MDB2_Statement_mssql extends MDB2_Statement_Common
  957. {
  958.  
  959. }
  960. // }}}
  961.  
  962. ?>
  963.