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

Source for file sqlsrv.php

Documentation is available at sqlsrv.php

  1. <?php
  2. // vim: set et ts=4 sw=4 fdm=marker:
  3. // +----------------------------------------------------------------------+
  4. // | PHP versions 4 and 5                                                 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1998-2008 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. // {{{ Class MDB2_Driver_sqlsrv
  46. /**
  47.  * MDB2 MSSQL Server (native) driver
  48.  *
  49.  * @package MDB2
  50.  * @category Database
  51.  */
  52. class MDB2_Driver_sqlsrv extends MDB2_Driver_Common
  53. {
  54.     // {{{ properties
  55.  
  56.     var $string_quoting = array('start' => "'"'end' => "'"'escape' => "'"'escape_pattern' => false);
  57.  
  58.     var $identifier_quoting = array('start' => '[''end' => ']''escape' => ']');
  59.  
  60.     var $connection = null;
  61.  
  62.     // }}}
  63.     // {{{ constructor
  64.  
  65.     /**
  66.      * Constructor
  67.      */
  68.     function __construct()
  69.     {
  70.         parent::__construct();
  71.  
  72.         $this->phptype 'sqlsrv';
  73.         $this->dbsyntax 'sqlsrv';
  74.  
  75.         $this->supported['sequences''emulated';
  76.         $this->supported['indexes'= true;
  77.         $this->supported['affected_rows'= true;
  78.         $this->supported['summary_functions'= true;
  79.         $this->supported['transactions'= true;
  80.         $this->supported['order_by_text'= true;
  81.         $this->supported['savepoints'= false;
  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['triggers'= true;
  88.         $this->supported['auto_increment'= true;
  89.         $this->supported['primary_key'= true;
  90.         $this->supported['result_introspection'= true;
  91.         $this->supported['prepared_statements''emulated';
  92.         $this->supported['identifier_quoting'= false;
  93.         $this->supported['pattern_escaping'= true;
  94.         $this->supported['new_link'= true;
  95.  
  96.         $this->options['DBA_username'= false;
  97.         $this->options['DBA_password'= false;
  98.         $this->options['database_device'= false;
  99.         $this->options['database_size'= false;
  100.         $this->options['max_identifiers_length'= 128; // MS Access: 64
  101.     }
  102.  
  103.     // }}}
  104.     // {{{ errorInfo()
  105.  
  106.     /**
  107.      * This method is used to collect information about an error
  108.      *
  109.      * @param integer $error 
  110.      * @return array 
  111.      * @access public
  112.      */
  113.     function errorInfo($error = null$connection = null)
  114.     {
  115.         if (null === $connection{
  116.             $connection $this->connection;
  117.         }
  118.  
  119.         $native_code = null;
  120.         $native_msg  = null;
  121.         if ($connection{
  122.             $retErrors = sqlsrv_errors(SQLSRV_ERR_ALL);
  123.             if ($retErrors !== null{
  124.                 foreach ($retErrors as $arrError{
  125.                     $native_msg .= "SQLState: ".$arrError'SQLSTATE']."\n";
  126.                     $native_msg .= "Error Code: ".$arrError'code']."\n";
  127.                     $native_msg .= "Message: ".$arrError'message']."\n";
  128.                     $native_code $arrError'code'];
  129.                 }
  130.             }
  131.         }
  132.         if (null === $error{
  133.             static $ecode_map;
  134.             if (empty($ecode_map)) {
  135.                 $ecode_map = array(
  136.                     102   => MDB2_ERROR_SYNTAX,
  137.                     110   => MDB2_ERROR_VALUE_COUNT_ON_ROW,
  138.                     155   => MDB2_ERROR_NOSUCHFIELD,
  139.                     156   => MDB2_ERROR_SYNTAX,
  140.                     170   => MDB2_ERROR_SYNTAX,
  141.                     207   => MDB2_ERROR_NOSUCHFIELD,
  142.                     208   => MDB2_ERROR_NOSUCHTABLE,
  143.                     245   => MDB2_ERROR_INVALID_NUMBER,
  144.                     319   => MDB2_ERROR_SYNTAX,
  145.                     321   => MDB2_ERROR_NOSUCHFIELD,
  146.                     325   => MDB2_ERROR_SYNTAX,
  147.                     336   => MDB2_ERROR_SYNTAX,
  148.                     515   => MDB2_ERROR_CONSTRAINT_NOT_NULL,
  149.                     547   => MDB2_ERROR_CONSTRAINT,
  150.                     911   => MDB2_ERROR_NOT_FOUND,
  151.                     1018  => MDB2_ERROR_SYNTAX,
  152.                     1035  => MDB2_ERROR_SYNTAX,
  153.                     1801  => MDB2_ERROR_ALREADY_EXISTS,
  154.                     1913  => MDB2_ERROR_ALREADY_EXISTS,
  155.                     2209  => MDB2_ERROR_SYNTAX,
  156.                     2223  => MDB2_ERROR_SYNTAX,
  157.                     2248  => MDB2_ERROR_SYNTAX,
  158.                     2256  => MDB2_ERROR_SYNTAX,
  159.                     2257  => MDB2_ERROR_SYNTAX,
  160.                     2627  => MDB2_ERROR_CONSTRAINT,
  161.                     2714  => MDB2_ERROR_ALREADY_EXISTS,
  162.                     3607  => MDB2_ERROR_DIVZERO,
  163.                     3701  => MDB2_ERROR_NOSUCHTABLE,
  164.                     7630  => MDB2_ERROR_SYNTAX,
  165.                     8134  => MDB2_ERROR_DIVZERO,
  166.                     9303  => MDB2_ERROR_SYNTAX,
  167.                     9317  => MDB2_ERROR_SYNTAX,
  168.                     9318  => MDB2_ERROR_SYNTAX,
  169.                     9331  => MDB2_ERROR_SYNTAX,
  170.                     9332  => MDB2_ERROR_SYNTAX,
  171.                     15253 => MDB2_ERROR_SYNTAX,
  172.                 );
  173.             }
  174.             if (isset($ecode_map[$native_code])) {
  175.                 if ($native_code == 3701
  176.                     && preg_match('/Cannot drop the index/i'$native_msg)
  177.                 {
  178.                    $error = MDB2_ERROR_NOT_FOUND;
  179.                 else {
  180.                     $error $ecode_map[$native_code];
  181.                 }
  182.             }
  183.         }
  184.         return array($error$native_code$native_msg);
  185.     }
  186.  
  187.     // }}}
  188.     // {{{ function escapePattern($text)
  189.  
  190.     /**
  191.      * Quotes pattern (% and _) characters in a string)
  192.      *
  193.      * @param   string  the input string to quote
  194.      *
  195.      * @return  string  quoted string
  196.      *
  197.      * @access  public
  198.      */
  199.     function escapePattern($text)
  200.     {
  201.         $text str_replace("[""[ [ ]"$text);
  202.         foreach ($this->wildcards as $wildcard{
  203.             $text str_replace($wildcard'[' $wildcard ']'$text);
  204.         }
  205.         return $text;
  206.     }
  207.  
  208.     // }}}
  209.     // {{{ beginTransaction()
  210.  
  211.     /**
  212.      * Start a transaction or set a savepoint.
  213.      *
  214.      * @param   string  name of a savepoint to set
  215.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  216.      *
  217.      * @access  public
  218.      */
  219.     function beginTransaction($savepoint = null)
  220.     {
  221.         $this->debug('Starting transaction/savepoint'__FUNCTION__array('is_manip' => true'savepoint' => $savepoint));
  222.         if (null !== $savepoint{
  223.             if (!$this->in_transaction{
  224.                 return $this->raiseError(MDB2_ERROR_INVALIDnullnull,
  225.                     'savepoint cannot be released when changes are auto committed'__FUNCTION__);
  226.             }
  227.             $query 'SAVE TRANSACTION '.$savepoint;
  228.             return $this->_doQuery($querytrue);
  229.         }
  230.         if ($this->in_transaction{
  231.             return MDB2_OK;  //nothing to do
  232.         }
  233.         if (!$this->destructor_registered && $this->opened_persistent{
  234.             $this->destructor_registered = true;
  235.             register_shutdown_function('MDB2_closeOpenTransactions');
  236.         }
  237.         if (MDB2::isError(sqlsrv_begin_transaction($this->connection))) {
  238.             return MDB2_ERROR;
  239.         }
  240.         $this->in_transaction = true;
  241.         return MDB2_OK;
  242.     }
  243.  
  244.     // }}}
  245.     // {{{ commit()
  246.  
  247.     /**
  248.      * Commit the database changes done during a transaction that is in
  249.      * progress or release a savepoint. This function may only be called when
  250.      * auto-committing is disabled, otherwise it will fail. Therefore, a new
  251.      * transaction is implicitly started after committing the pending changes.
  252.      *
  253.      * @param   string  name of a savepoint to release
  254.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  255.      *
  256.      * @access  public
  257.      */
  258.     function commit($savepoint = null)
  259.     {
  260.         $this->debug('Committing transaction/savepoint'__FUNCTION__array('is_manip' => true'savepoint' => $savepoint));
  261.         if (!$this->in_transaction{
  262.             return $this->raiseError(MDB2_ERROR_INVALIDnullnull,
  263.                 'commit/release savepoint cannot be done changes are auto committed'__FUNCTION__);
  264.         }
  265.         if (null !== $savepoint{
  266.             return MDB2_OK;
  267.         }
  268.  
  269.         if (MDB2::isError(sqlsrv_commit($this->connection))) {
  270.             return MDB2_ERROR;
  271.         }
  272.         $this->in_transaction = false;
  273.         return MDB2_OK;
  274.     }
  275.  
  276.     // }}}
  277.     // {{{ rollback()
  278.  
  279.     /**
  280.      * Cancel any database changes done during a transaction or since a specific
  281.      * savepoint that is in progress. This function may only be called when
  282.      * auto-committing is disabled, otherwise it will fail. Therefore, a new
  283.      * transaction is implicitly started after canceling the pending changes.
  284.      *
  285.      * @param   string  name of a savepoint to rollback to
  286.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  287.      *
  288.      * @access  public
  289.      */
  290.     function rollback($savepoint = null)
  291.     {
  292.         $this->debug('Rolling back transaction/savepoint'__FUNCTION__array('is_manip' => true'savepoint' => $savepoint));
  293.         if (!$this->in_transaction{
  294.             return $this->raiseError(MDB2_ERROR_INVALIDnullnull,
  295.                 'rollback cannot be done changes are auto committed'__FUNCTION__);
  296.         }
  297.         if (null !== $savepoint{
  298.             $query 'ROLLBACK TRANSACTION '.$savepoint;
  299.             return $this->_doQuery($querytrue);
  300.         }
  301.  
  302.         if (MDB2::isError(sqlsrv_rollback($this->connection))) {
  303.             return MDB2_ERROR;
  304.         }
  305.         $this->in_transaction = false;
  306.         return MDB2_OK;
  307.     }
  308.  
  309.     // }}}
  310.     // {{{ _doConnect()
  311.  
  312.     /**
  313.      * do the grunt work of the connect
  314.      *
  315.      * @return connection on success or MDB2 Error Object on failure
  316.      * @access protected
  317.      */
  318.     function _doConnect($username$password$database=null$persistent = false)
  319.     {
  320.         if (!extension_loaded($this->phptype)) {
  321.             return $this->raiseError(MDB2_ERROR_NOT_FOUNDnullnull,
  322.                 'extension '.$this->phptype.' is not installed PHP'__FUNCTION__);
  323.         }
  324.  
  325.         $host $this->dsn['hostspec'$this->dsn['hostspec''.\\SQLEXPRESS';
  326.         $params = array(
  327.             'UID' => $username $username : null,
  328.             'PWD' => $password $password : null,
  329.         );
  330.         if ($database{
  331.             $params['Database'$database;
  332.         }
  333.  
  334.         if ($this->dsn['port'&& $this->dsn['port'!= 1433{
  335.             $host .= ','.$this->dsn['port'];
  336.         }
  337.  
  338.         $connection @sqlsrv_connect($host$params);
  339.         if (!$connection{
  340.             return $this->raiseError(MDB2_ERROR_CONNECT_FAILEDnullnull,
  341.                 'unable to establish a connection'__FUNCTION____FUNCTION__);
  342.         }
  343.         if (null !== $database{
  344.             $this->connected_database_name $database;
  345.         }
  346.  
  347.         if (!empty($this->dsn['charset'])) {
  348.             $result $this->setCharset($this->dsn['charset']$connection);
  349.             if (MDB2::isError($result)) {
  350.                 return $result;
  351.             }
  352.         }
  353.  
  354.        if (empty($this->dsn['disable_iso_date'])) {
  355.            @sqlsrv_query($connection,'SET DATEFORMAT ymd');
  356.        }
  357.  
  358.        return $connection;
  359.     }
  360.  
  361.     // }}}
  362.     // {{{ connect()
  363.  
  364.     /**
  365.      * Connect to the database
  366.      *
  367.      * @return true on success, MDB2 Error Object on failure
  368.      */
  369.     function connect()
  370.     {
  371.         if (is_resource($this->connection)) {
  372.             if (MDB2::areEquals($this->connected_dsn$this->dsn)) {
  373.                 return MDB2_OK;
  374.             }
  375.             $this->disconnect(false);
  376.         }
  377.  
  378.         $connection $this->_doConnect(
  379.             $this->dsn['username'],
  380.             $this->dsn['password'],
  381.             $this->database_name,
  382.             $this->options['persistent']
  383.         );
  384.         if (MDB2::isError($connection)) {
  385.             return $connection;
  386.         }
  387.  
  388.         $this->connection = $connection;
  389.         $this->connected_dsn $this->dsn;
  390.         $this->connected_database_name $this->database_name;
  391.         $this->opened_persistent $this->options['persistent'];
  392.         $this->dbsyntax $this->dsn['dbsyntax'$this->dsn['dbsyntax'$this->phptype;
  393.  
  394.         return MDB2_OK;
  395.     }
  396.  
  397.     // }}}
  398.     // {{{ databaseExists()
  399.  
  400.     /**
  401.      * check if given database name exists?
  402.      *
  403.      * @param string $name    name of the database that should be checked
  404.      *
  405.      * @return mixed true/false on success, a MDB2 error on failure
  406.      * @access public
  407.      */
  408.     function databaseExists($name)
  409.     {
  410.         $connection $this->_doConnect($this->dsn['username'],$this->dsn['password']);
  411.         if (MDB2::isError($connection)) {
  412.             return MDB2_ERROR_CONNECT_FAILED;
  413.         }
  414.         $result @sqlsrv_query($connection,'select name from master..sysdatabases where name = \''.strtolower($name).'\'');
  415.         if (@sqlsrv_fetch($result)) {
  416.             return true;
  417.         }
  418.         return MDB2_ERROR_NOT_FOUND;
  419.     }
  420.  
  421.     // }}}
  422.     // {{{ disconnect()
  423.  
  424.     /**
  425.      * Log out and disconnect from the database.
  426.      *
  427.      * @param  boolean $force if the disconnect should be forced even if the
  428.      *                         connection is opened persistently
  429.      * @return mixed true on success, false if not connected and error
  430.      *                 object on error
  431.      * @access public
  432.      */
  433.     function disconnect($force = true)
  434.     {
  435.         if (is_resource($this->connection)) {
  436.             if ($this->in_transaction{
  437.                 $dsn $this->dsn;
  438.                 $database_name $this->database_name;
  439.                 $persistent $this->options['persistent'];
  440.                 $this->dsn $this->connected_dsn;
  441.                 $this->database_name $this->connected_database_name;
  442.                 $this->options['persistent'$this->opened_persistent;
  443.                 $this->rollback();
  444.                 $this->dsn $dsn;
  445.                 $this->database_name $database_name;
  446.                 $this->options['persistent'$persistent;
  447.             }
  448.  
  449.             @sqlsrv_close($this->connection);
  450.         }
  451.         return parent::disconnect($force);
  452.     }
  453.  
  454.     // }}}
  455.     // {{{ standaloneQuery()
  456.  
  457.    /**
  458.      * execute a query as DBA
  459.      *
  460.      * @param string $query the SQL query
  461.      * @param mixed   $types  array that contains the types of the columns in
  462.      *                         the result set
  463.      * @param boolean $is_manip  if the query is a manipulation query
  464.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  465.      * @access public
  466.      */
  467.     function &standaloneQuery($query$types = null$is_manip = false)
  468.     {
  469.         $user $this->options['DBA_username']$this->options['DBA_username'$this->dsn['username'];
  470.         $pass $this->options['DBA_password']$this->options['DBA_password'$this->dsn['password'];
  471.         $connection $this->_doConnect($user$pass$this->database_name$this->options['persistent']);
  472.         if (MDB2::isError($connection)) {
  473.             return $connection;
  474.         }
  475.  
  476.         $query $this->_modifyQuery($query$is_manip$this->limit$this->offset);
  477.         $this->offset $this->limit = 0;
  478.  
  479.         $result $this->_doQuery($query$is_manip$connection);
  480.         if (!MDB2::isError($result)) {
  481.             $result $this->_affectedRows($connection$result);
  482.         }
  483.  
  484.         @sqlsrv_close($connection);
  485.         return $result;
  486.     }
  487.  
  488.     // }}}
  489.     // {{{ _doQuery()
  490.  
  491.     /**
  492.      * Execute a query
  493.      * @param string $query  query
  494.      * @param boolean $is_manip  if the query is a manipulation query
  495.      * @param resource $connection 
  496.      * @param string $database_name 
  497.      * @return result or error object
  498.      * @access protected
  499.      */
  500.     function _doQuery($query$is_manip = false$connection = null$database_name = null)
  501.     {
  502.         $this->last_query $query;
  503.         $result $this->debug($query'query'array('is_manip' => $is_manip'when' => 'pre'));
  504.         if ($result{
  505.             if (MDB2::isError($result)) {
  506.                 return $result;
  507.             }
  508.             $query $result;
  509.         }
  510.         if ($this->options['disable_query']{
  511.             $result $is_manip ? 0 : null;
  512.             return $result;
  513.         }
  514.  
  515.         if (null === $connection{
  516.             $connection $this->getConnection();
  517.             if (MDB2::isError($connection)) {
  518.                 return $connection;
  519.             }
  520.         }
  521.         if (null === $database_name{
  522.             $database_name $this->database_name;
  523.         }
  524.  
  525.         if ($database_name && $database_name != $this->connected_database_name{
  526.             $connection $this->_doConnect($this->dsn['username'],$this->dsn['password'],$database_name);
  527.             if (MDB2::isError($connection)) {
  528.                 $err $this->raiseError(nullnullnull,
  529.                     'Could not select the database: '.$database_name__FUNCTION__);
  530.                 return $err;
  531.             }
  532.             $this->connected_database_name $database_name;
  533.         }
  534.  
  535.     $query preg_replace('/DATE_FORMAT\((MIN\()?([\w|.]*)(\))?\\Q, \'%Y-%m-%d\')\E/i','CONVERT(varchar(10),$1$2$3,120)',$query);
  536.     $query preg_replace('/DATE_FORMAT\(([\w|.]*)\, \'\%Y\-\%m\-\%d %H\:00\:00\'\)/i','CONVERT(varchar(13),$1,120)+\':00:00\'',$query);
  537.         $result @sqlsrv_query($connection,$query);
  538.         if (!$result{
  539.             $err $this->raiseError(nullnullnull,
  540.                 'Could not execute statement'__FUNCTION__);
  541.             return $err;
  542.         }
  543.         $this->result $result;
  544.         $this->debug($query'query'array('is_manip' => $is_manip'when' => 'post''result' => $result));
  545.         return $result;
  546.     }
  547.  
  548.     // }}}
  549.     // {{{ _affectedRows()
  550.  
  551.     /**
  552.      * Returns the number of rows affected
  553.      *
  554.      * @param resource $result 
  555.      * @param resource $connection 
  556.      * @return mixed MDB2 Error Object or the number of rows affected
  557.      * @access private
  558.      */
  559.     function _affectedRows($connection$result = null)
  560.     {
  561.         if (null === $result{
  562.             $result $this->result;
  563.         }
  564.         return sqlsrv_rows_affected($this->result);
  565.     }
  566.  
  567.     // }}}
  568.     // {{{ _modifyQuery()
  569.  
  570.     /**
  571.      * Changes a query string for various DBMS specific reasons
  572.      *
  573.      * @param string $query  query to modify
  574.      * @param boolean $is_manip  if it is a DML query
  575.      * @param integer $limit  limit the number of rows
  576.      * @param integer $offset  start reading from given offset
  577.      * @return string modified query
  578.      * @access protected
  579.      */
  580.     function _modifyQuery($query$is_manip$limit$offset)
  581.     {
  582.         if ($limit > 0{
  583.             $fetch $offset $limit;
  584.             if (!$is_manip{
  585.                 return preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',
  586.                     "\\1SELECT\\2 TOP $fetch"$query);
  587.             }
  588.         }
  589.         return $query;
  590.     }
  591.  
  592.     // }}}
  593.     // {{{ getServerVersion()
  594.  
  595.     /**
  596.      * return version information about the server
  597.      *
  598.      * @param bool   $native  determines if the raw version string should be returned
  599.      * @return mixed array/string with version information or MDB2 error object
  600.      * @access public
  601.      */
  602.     function getServerVersion($native = false)
  603.     {
  604.         if ($this->connected_server_info{
  605.             $server_info $this->connected_server_info;
  606.         else {
  607.             $this->connect();
  608.             $server_info = sqlsrv_server_info($this->connection);
  609.         }
  610.         // cache server_info
  611.         $this->connected_server_info $server_info;
  612.         $version $server_info['SQLServerVersion'];
  613.         if (!$native{
  614.             if (preg_match('/(\d+)\.(\d+)\.(\d+)/'$version$tmp)) {
  615.                 $version = array(
  616.                     'major' => $tmp[1],
  617.                     'minor' => $tmp[2],
  618.                     'patch' => $tmp[3],
  619.                     'extra' => null,
  620.                     'native' => $version,
  621.                 );
  622.             else {
  623.                 $version = array(
  624.                     'major' => null,
  625.                     'minor' => null,
  626.                     'patch' => null,
  627.                     'extra' => null,
  628.                     'native' => $version,
  629.                 );
  630.             }
  631.         }
  632.         return $version;
  633.     }
  634.  
  635.     // }}}
  636.     // {{{ _checkSequence
  637.  
  638.     /**
  639.      * Checks if there's a sequence that exists.
  640.      *
  641.      * @param  string $seq_name    The sequence name to verify.
  642.      * @return bool   $tableExists The value if the table exists or not
  643.      * @access private
  644.      */
  645.     function _checkSequence($seq_name)
  646.     {
  647.         $query = "SELECT * FROM $seq_name";
  648.         $tableExists =$this->_doQuery($querytrue);
  649.         if (MDB2::isError($tableExists)) {
  650.             if ($tableExists->getCode(== MDB2_ERROR_NOSUCHTABLE{
  651.                 return false;
  652.             }
  653.             return false;
  654.         }
  655.         if (@sqlsrv_fetch($tableExists)) {
  656.             return true;
  657.         }
  658.         return false;
  659.     }
  660.  
  661.     // }}}
  662.     // {{{ nextID()
  663.  
  664.     /**
  665.      * Returns the next free id of a sequence
  666.      *
  667.      * @param string $seq_name name of the sequence
  668.      * @param boolean $ondemand when true the sequence is
  669.      *                           automatic created, if it
  670.      *                           not exists
  671.      *
  672.      * @return mixed MDB2 Error Object or id
  673.      * @access public
  674.      */
  675.     function nextID($seq_name$ondemand = true)
  676.     {
  677.         $sequence_name $this->quoteIdentifier($this->getSequenceName($seq_name)true);
  678.         $seqcol_name $this->quoteIdentifier($this->options['seqcol_name']true);
  679.         $this->pushErrorHandling(PEAR_ERROR_RETURN);
  680.         $this->expectError(MDB2_ERROR_NOSUCHTABLE);
  681.  
  682.         $seq_val $this->_checkSequence($sequence_name);
  683.  
  684.         if ($seq_val{
  685.             $query = "SET IDENTITY_INSERT $sequence_name OFF ".
  686.                      "INSERT INTO $sequence_name DEFAULT VALUES";
  687.         else {
  688.             $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (0)";
  689.         }
  690.         $result $this->_doQuery($querytrue);
  691.         $this->popExpect();
  692.         $this->popErrorHandling();
  693.         if (MDB2::isError($result)) {
  694.             if ($ondemand && !$this->_checkSequence($sequence_name)) {
  695.                 $this->loadModule('Manager'nulltrue);
  696.                 $result $this->manager->createSequence($seq_name);
  697.                 if (MDB2::isError($result)) {
  698.                     return $this->raiseError($resultnullnull,
  699.                         'on demand sequence '.$seq_name.' could not be created'__FUNCTION__);
  700.                 else {
  701.                     /**
  702.                      * Little off-by-one problem with the sequence emulation
  703.                      * here being fixed, that instead of re-calling nextID
  704.                      * and forcing an increment by one, we simply check if it
  705.                      * exists, then we get the last inserted id if it does.
  706.                      *
  707.                      * In theory, $seq_name should be created otherwise there would
  708.                      * have been an error thrown somewhere up there..
  709.                      *
  710.                      * @todo confirm
  711.                      */
  712.                     if ($this->_checkSequence($seq_name)) {
  713.                         return $this->lastInsertID($seq_name);
  714.                     }
  715.  
  716.                     return $this->nextID($seq_namefalse);
  717.                 }
  718.             }
  719.             return $result;
  720.         }
  721.         $value $this->lastInsertID($sequence_name);
  722.         if (is_numeric($value)) {
  723.             $query = "DELETE FROM $sequence_name WHERE $seqcol_name < $value";
  724.             $result $this->_doQuery($querytrue);
  725.             if (MDB2::isError($result)) {
  726.                 $this->warnings['nextID: could not delete previous sequence table values from '.$seq_name;
  727.             }
  728.         }
  729.         return $value;
  730.     }
  731.  
  732.     // }}}
  733.     // {{{ lastInsertID()
  734.  
  735.     /**
  736.      * Returns the autoincrement ID if supported or $id or fetches the current
  737.      * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
  738.      *
  739.      * @param string $table name of the table into which a new row was inserted
  740.      * @param string $field name of the field into which a new row was inserted
  741.      *
  742.      * @return mixed MDB2 Error Object or id
  743.      * @access public
  744.      */
  745.     function lastInsertID($table = null$field = null)
  746.     {
  747.         return $this->queryOne("SELECT IDENT_CURRENT('$table')"'integer');
  748.     }
  749.  
  750.     // }}}
  751. }
  752.  
  753. // }}}
  754. // {{{ Class MDB2_Result_mssql
  755.  
  756. /**
  757.  * MDB2 MSSQL Server result driver
  758.  *
  759.  * @package MDB2
  760.  * @category Database
  761.  * @author  Frank M. Kromann <frank@kromann.info>
  762.  */
  763. class MDB2_Result_sqlsrv extends MDB2_Result_Common
  764. {
  765.     // {{{ constructor: function __construct($db, $result, $limit = 0, $offset = 0)
  766.  
  767.     /**
  768.      * Constructor
  769.      */
  770.     function __construct($db$result$limit = 0$offset = 0)
  771. {
  772.         $this->db $db;
  773.         $this->result $result;
  774.         $this->offset $offset;
  775.         $this->limit max(0$limit - 1);
  776.         $this->cursor = 0;
  777.         $this->rows = array();
  778.         $this->numFields = sqlsrv_num_fields($result);
  779.         $this->fieldMeta = sqlsrv_field_metadata($result);
  780.         $this->numRowsAffected = sqlsrv_rows_affected($result);
  781.         while ($row = sqlsrv_fetch_array($resultSQLSRV_FETCH_ASSOC)) {
  782.             if ($row !== null{
  783.                 if ($this->offset && $this->offset_count $this->offset{
  784.                     $this->offset_count++;
  785.                     continue;
  786.                 }
  787.                 foreach ($row as $k => $v{
  788.                     if (is_object($v&& method_exists($v'format')) {
  789.                         //DateTime Object
  790.                         $row[$k$v->format('Y-m-d H:i:s');
  791.                     }
  792.                 }
  793.                 $this->rows[$row//read results into memory, cursors are not supported
  794.             }
  795.         }
  796.         $this->rowcnt count($this->rows);
  797.     }
  798.  
  799.     // }}}
  800.     // {{{ _skipLimitOffset()
  801.  
  802.     /**
  803.      * Skip the first row of a result set.
  804.      *
  805.      * @param resource $result 
  806.      * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
  807.      * @access protected
  808.      */
  809. /*    function _skipLimitOffset()
  810.     {
  811.         if ($this->limit) {
  812.             if ($this->rownum >= $this->limit) {
  813.                 return false;
  814.             }
  815.         }
  816.         if ($this->offset) {
  817.             while ($this->offset_count < $this->offset) {
  818.                 ++$this->offset_count;
  819.                 if (!is_array(@sqlsrv_fetch_array($this->result))) {
  820.                     $this->offset_count = $this->limit;
  821.                     return false;
  822.                 }
  823.             }
  824.         }
  825.         return MDB2_OK;
  826.     }*/
  827.  
  828.     // }}}
  829.     function array_to_obj($array&$obj{
  830.         foreach ($array as $key => $value{
  831.             if (is_array($value)) {
  832.                 $obj->$key = new stdClass();
  833.                 array_to_obj($value$obj->$key);
  834.             else {
  835.                 $obj->$key $value;
  836.             }
  837.         }
  838.         return $obj;
  839.     }
  840.     // {{{ fetchRow()
  841.  
  842.     /**
  843.      * Fetch a row and insert the data into an existing array.
  844.      *
  845.      * @param int       $fetchmode  how the array data should be indexed
  846.      * @param int    $rownum    number of the row where the data can be found
  847.      * @return int data array on success, a MDB2 error on failure
  848.      * @access public
  849.      */
  850.     function fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT$rownum = null)
  851.     {
  852.         if (!$this->result{
  853.             return $this->db->raiseError(MDB2_ERROR_INVALIDnullnull'no valid statement given'__FUNCTION__);
  854.         }
  855.         if (($this->limit && $this->rownum >= $this->limit|| ($this->cursor >= $this->rowcnt || $this->rowcnt == 0)) {
  856.             return null;
  857.         }
  858.         if (null !== $rownum{
  859.             $seek $this->seek($rownum);
  860.             if (MDB2::isError($seek)) {
  861.                 return $seek;
  862.             }
  863.         }
  864.         if ($fetchmode == MDB2_FETCHMODE_DEFAULT{
  865.             $fetchmode $this->db->fetchmode;
  866.         }
  867.  
  868.         $row = false;
  869.         $arrNum = array();
  870.         if ($fetchmode == MDB2_FETCHMODE_ORDERED{
  871.             foreach ($this->rows[$this->cursoras $key=>$value{
  872.                 $arrNum[$value;
  873.             }
  874.         }
  875.         switch($fetchmode{
  876.             case MDB2_FETCHMODE_ASSOC:
  877.                 $row $this->rows[$this->cursor];
  878.                 break;
  879.             case MDB2_FETCHMODE_ORDERED:
  880.                 $row $arrNum;
  881.                 break;
  882.             case MDB2_FETCHMODE_OBJECT:
  883.                 $o = new $this->db->options['fetch_class'];
  884.                 $row $this->array_to_obj($this->rows[$this->cursor]$o);
  885.                 break;
  886.         }
  887.         $this->cursor++;
  888.  
  889.         /*
  890.         if ($fetchmode == MDB2_FETCHMODE_OBJECT) {
  891.             $row = sqlsrv_fetch_object($this->result,$this->db->options['fetch_class']);
  892.         } else {
  893.         switch($fetchmode) {
  894.             case MDB2_FETCHMODE_ASSOC: $fetchmode = SQLSRV_FETCH_ASSOC; break;
  895.             case MDB2_FETCHMODE_ORDERED: $fetchmode = SQLSRV_FETCH_NUMERIC; break;
  896.             case MDB2_FETCHMODE_DEFAULT:
  897.             default:
  898.                 $fetchmode = SQLSRV_FETCH_BOTH;
  899.         }
  900.             $row = sqlsrv_fetch_array($this->result,$fetchmode);
  901.         }
  902.         foreach ($row as $key=>$value) {
  903.             if (is_object($value) && method_exists($value, 'format')) {//is DateTime object
  904.                 $row[$key] = $value->format("Y-m-d H:i:s");
  905.             }
  906.         }*/
  907.  
  908.         /*if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
  909.             $fetchmode = $this->db->fetchmode;
  910.         }*/
  911.         if ($fetchmode == MDB2_FETCHMODE_ASSOC && is_array($row&& $this->db->options['portability'MDB2_PORTABILITY_FIX_CASE{
  912.             $row array_change_key_case($row$this->db->options['field_case']);
  913.         }
  914.         if (!$row{
  915.             if (false === $this->result{
  916.                 $err $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  917.                     'resultset has already been freed'__FUNCTION__);
  918.                 return $err;
  919.             }
  920.             return null;
  921.         }
  922.         $mode $this->db->options['portability'MDB2_PORTABILITY_EMPTY_TO_NULL;
  923.         $rtrim = false;
  924.         if ($this->db->options['portability'MDB2_PORTABILITY_RTRIM{
  925.             if (empty($this->types)) {
  926.                 $mode += MDB2_PORTABILITY_RTRIM;
  927.             else {
  928.                 $rtrim = true;
  929.             }
  930.         }
  931.         if ($mode{
  932.             $this->db->_fixResultArrayValues($row$mode);
  933.         }
  934.         if (   (   $fetchmode != MDB2_FETCHMODE_ASSOC
  935.                 && $fetchmode != MDB2_FETCHMODE_OBJECT)
  936.             && !empty($this->types)
  937.         {
  938.             $row $this->db->datatype->convertResultRow($this->types$row$rtrim);
  939.         elseif (($fetchmode == MDB2_FETCHMODE_ASSOC
  940.                 || $fetchmode == MDB2_FETCHMODE_OBJECT)
  941.             && !empty($this->types_assoc)
  942.         {
  943.             $row $this->db->datatype->convertResultRow($this->types_assoc$row$rtrim);
  944.         }
  945.         if (!empty($this->values)) {
  946.             $this->_assignBindColumns($row);
  947.         }
  948.         ++$this->rownum;
  949.         return $row;
  950.     }
  951.  
  952.     // }}}
  953.     // {{{ _getColumnNames()
  954.  
  955.     /**
  956.      * Retrieve the names of columns returned by the DBMS in a query result.
  957.      *
  958.      * @return  mixed   Array variable that holds the names of columns as keys
  959.      *                   or an MDB2 error on failure.
  960.      *                   Some DBMS may not return any columns when the result set
  961.      *                   does not contain any rows.
  962.      * @access private
  963.      */
  964.     function _getColumnNames()
  965.     {
  966.         if (!$this->result{
  967.             return $this->db->raiseError(MDB2_ERROR_INVALIDnullnull'no valid statement given'__FUNCTION__);
  968.         }
  969.         $columns = array();
  970.         foreach ($this->fieldMeta as $n => $col{
  971.             $columns[$col['Name']] $n;
  972.         }
  973.         if ($this->db->options['portability'MDB2_PORTABILITY_FIX_CASE{
  974.             $columns array_change_key_case($columns$this->db->options['field_case']);
  975.         }
  976.         return $columns;
  977.     }
  978.  
  979.     // }}}
  980.     // {{{ numCols()
  981.  
  982.     /**
  983.      * Count the number of columns returned by the DBMS in a query result.
  984.      *
  985.      * @return mixed integer value with the number of columns, a MDB2 error
  986.      *       on failure
  987.      * @access public
  988.      */
  989.     function numCols()
  990.     {
  991.         if (!$this->result{
  992.             return $this->db->raiseError(MDB2_ERROR_INVALIDnullnull'no valid statement given'__FUNCTION__);
  993.         }
  994.         $cols $this->numFields;
  995.         if (!$cols{
  996.             if (false === $this->result{
  997.                 return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  998.                     'resultset has already been freed'__FUNCTION__);
  999.             }
  1000.             if (null === $this->result{
  1001.                 return count($this->types);
  1002.             }
  1003.             return $this->db->raiseError(nullnullnull,
  1004.                 'Could not get column count'__FUNCTION__);
  1005.         }
  1006.         return $cols;
  1007.     }
  1008.  
  1009.     // }}}
  1010.     // {{{ nextResult()
  1011.  
  1012.     /**
  1013.      * Move the internal result pointer to the next available result
  1014.      *
  1015.      * @return true on success, false if there is no more result set or an error object on failure
  1016.      * @access public
  1017.      */
  1018.     function nextResult()
  1019.     {
  1020.         if (false === $this->result{
  1021.             return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  1022.                 'resultset has already been freed'__FUNCTION__);
  1023.         }
  1024.         if (null === $this->result{
  1025.             return false;
  1026.         }
  1027.         $ret = sqlsrv_next_result($this->result);
  1028.         if ($ret{
  1029.             $this->cursor = 0;
  1030.             $this->rows = array();
  1031.             $this->numFields = sqlsrv_num_fields($this->result);
  1032.             $this->fieldMeta = sqlsrv_field_metadata($this->result);
  1033.             $this->numRowsAffected = sqlsrv_rows_affected($this->result);
  1034.             while ($row = sqlsrv_fetch_array($this->resultSQLSRV_FETCH_ASSOC)) {
  1035.                 if ($row !== null{
  1036.                     if ($this->offset && $this->offset_count $this->offset{
  1037.                         $this->offset_count++;
  1038.                         continue;
  1039.                     }
  1040.                     foreach ($row as $k => $v{
  1041.                         if (is_object($v&& method_exists($v'format')) {//DateTime Object
  1042.                             //$v->setTimezone(new DateTimeZone('GMT'));//TS_ISO_8601 with a trailing 'Z' is GMT
  1043.                             $row[$k$v->format("Y-m-d H:i:s");
  1044.                         }
  1045.                     }
  1046.                     $this->rows[$row;//read results into memory, cursors are not supported
  1047.                 }
  1048.             }
  1049.             $this->rowcnt count($this->rows);
  1050.         }
  1051.         return $ret;
  1052.     }
  1053.  
  1054.     // }}}
  1055.     // {{{ free()
  1056.  
  1057.     /**
  1058.      * Free the internal resources associated with $result.
  1059.      *
  1060.      * @return boolean true on success, false if $result is invalid
  1061.      * @access public
  1062.      */
  1063.     function free()
  1064.     {
  1065.         if (is_resource($this->result&& $this->db->connection{
  1066.             if (!@sqlsrv_free_stmt($this->result)) {
  1067.                 return $this->db->raiseError(nullnullnull,
  1068.                     'Could not free result'__FUNCTION__);
  1069.             }
  1070.         }
  1071.         $this->result = false;
  1072.         return MDB2_OK;
  1073.     }
  1074.  
  1075.     // }}}
  1076.     // {{{ function rowCount()
  1077.     /**
  1078.      * Returns the actual row number that was last fetched (count from 0)
  1079.      * @return  int 
  1080.      *
  1081.      * @access  public
  1082.      */
  1083.     function rowCount()
  1084.     {
  1085.         return $this->cursor;
  1086. }
  1087.  
  1088. // }}}
  1089.     // {{{ function numRows()
  1090.  
  1091. /**
  1092.      * Returns the number of rows in a result object
  1093.      *
  1094.      * @return  mixed   MDB2 Error Object or the number of rows
  1095.  *
  1096.      * @access  public
  1097.  */
  1098.     function numRows()
  1099. {
  1100.         return $this->rowcnt;
  1101.     }
  1102.  
  1103.     // }}}
  1104.     // {{{ function seek($rownum = 0)
  1105.  
  1106.     /**
  1107.      * Seek to a specific row in a result set
  1108.      *
  1109.      * @param   int     number of the row where the data can be found
  1110.      *
  1111.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  1112.      *
  1113.      * @access public
  1114.      */
  1115.     function seek($rownum = 0)
  1116.     {
  1117.         $this->cursor min($rownum$this->rowcnt);
  1118.                 return MDB2_OK;
  1119.             }
  1120.  
  1121.     // }}}
  1122.     }
  1123.  
  1124.     // }}}
  1125. // {{{ class MDB2_BufferedResult_mssql
  1126.  
  1127. /**
  1128.  * MDB2 MSSQL Server buffered result driver
  1129.  *
  1130.  * @package MDB2
  1131.  * @category Database
  1132.  * @author  Frank M. Kromann <frank@kromann.info>
  1133.  */
  1134. {
  1135.     // {{{ valid()
  1136.  
  1137.     /**
  1138.      * Check if the end of the result set has been reached
  1139.      *
  1140.      * @return mixed true or false on sucess, a MDB2 error on failure
  1141.      * @access public
  1142.      */
  1143.     function valid()
  1144.     {
  1145.         $numrows $this->numRows();
  1146.         if (MDB2::isError($numrows)) {
  1147.             return $numrows;
  1148.         }
  1149.         return $this->rownum ($numrows - 1);
  1150.     }
  1151.  
  1152.     // }}}
  1153.  
  1154. }
  1155.  
  1156. // }}}
  1157. // {{{ MDB2_Statement_mssql
  1158.  
  1159. /**
  1160.  * MDB2 MSSQL Server statement driver
  1161.  *
  1162.  * @package MDB2
  1163.  * @category Database
  1164.  * @author  Frank M. Kromann <frank@kromann.info>
  1165.  */
  1166. class MDB2_Statement_sqlsrv extends MDB2_Statement_Common
  1167. {
  1168.  
  1169. }
  1170.  
  1171. // }}}
  1172.  
  1173. ?>

Documentation generated on Mon, 11 Mar 2019 15:51:28 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.