Source for file sqlsrv.php

Documentation is available at sqlsrv.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Platform
  4.  * @subpackage  Database
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE
  8.  */
  9.  
  10. defined('JPATH_PLATFORM'or die;
  11.  
  12. /**
  13.  * SQL Server database driver
  14.  *
  15.  * @package     Joomla.Platform
  16.  * @subpackage  Database
  17.  * @see         http://msdn.microsoft.com/en-us/library/cc296152(SQL.90).aspx
  18.  * @since       12.1
  19.  */
  20. {
  21.     /**
  22.      * The name of the database driver.
  23.      *
  24.      * @var    string 
  25.      * @since  12.1
  26.      */
  27.     public $name = 'sqlsrv';
  28.  
  29.     /**
  30.      * The character(s) used to quote SQL statement names such as table names or field names,
  31.      * etc.  The child classes should define this as necessary.  If a single character string the
  32.      * same character is used for both sides of the quoted name, else the first character will be
  33.      * used for the opening quote and the second for the closing quote.
  34.      *
  35.      * @var    string 
  36.      * @since  12.1
  37.      */
  38.     protected $nameQuote = '[]';
  39.  
  40.     /**
  41.      * The null or zero representation of a timestamp for the database driver.  This should be
  42.      * defined in child classes to hold the appropriate value for the engine.
  43.      *
  44.      * @var    string 
  45.      * @since  12.1
  46.      */
  47.     protected $nullDate = '1900-01-01 00:00:00';
  48.  
  49.     /**
  50.      * @var    string  The minimum supported database version.
  51.      * @since  12.1
  52.      */
  53.     protected static $dbMinimum '10.50.1600.1';
  54.  
  55.     /**
  56.      * Test to see if the SQLSRV connector is available.
  57.      *
  58.      * @return  boolean  True on success, false otherwise.
  59.      *
  60.      * @since   12.1
  61.      */
  62.     public static function isSupported()
  63.     {
  64.         return (function_exists('sqlsrv_connect'));
  65.     }
  66.  
  67.     /**
  68.      * Constructor.
  69.      *
  70.      * @param   array  $options  List of options used to configure the connection
  71.      *
  72.      * @since   12.1
  73.      */
  74.     public function __construct($options)
  75.     {
  76.         // Get some basic values from the options.
  77.         $options['host'(isset($options['host'])) $options['host''localhost';
  78.         $options['user'(isset($options['user'])) $options['user''';
  79.         $options['password'(isset($options['password'])) $options['password''';
  80.         $options['database'(isset($options['database'])) $options['database''';
  81.         $options['select'(isset($options['select'])) ? (bool) $options['select'true;
  82.  
  83.         // Finalize initialisation
  84.         parent::__construct($options);
  85.     }
  86.  
  87.     /**
  88.      * Destructor.
  89.      *
  90.      * @since   12.1
  91.      */
  92.     public function __destruct()
  93.     {
  94.         $this->disconnect();
  95.     }
  96.  
  97.     /**
  98.      * Connects to the database if needed.
  99.      *
  100.      * @return  void  Returns void if the database connected successfully.
  101.      *
  102.      * @since   12.1
  103.      * @throws  RuntimeException
  104.      */
  105.     public function connect()
  106.     {
  107.         if ($this->connection)
  108.         {
  109.             return;
  110.         }
  111.  
  112.         // Build the connection configuration array.
  113.         $config array(
  114.             'Database' => $this->options['database'],
  115.             'uid' => $this->options['user'],
  116.             'pwd' => $this->options['password'],
  117.             'CharacterSet' => 'UTF-8',
  118.             'ReturnDatesAsStrings' => true);
  119.  
  120.         // Make sure the SQLSRV extension for PHP is installed and enabled.
  121.         if (!function_exists('sqlsrv_connect'))
  122.         {
  123.             throw new RuntimeException('PHP extension sqlsrv_connect is not available.');
  124.         }
  125.  
  126.         // Attempt to connect to the server.
  127.         if (!($this->connection = sqlsrv_connect($this->options['host']$config)))
  128.         {
  129.             throw new RuntimeException('Database sqlsrv_connect failed');
  130.         }
  131.  
  132.         // Make sure that DB warnings are not returned as errors.
  133.         sqlsrv_configure('WarningsReturnAsErrors'0);
  134.  
  135.         // If auto-select is enabled select the given database.
  136.         if ($this->options['select'&& !empty($this->options['database']))
  137.         {
  138.             $this->select($this->options['database']);
  139.         }
  140.     }
  141.  
  142.     /**
  143.      * Disconnects the database.
  144.      *
  145.      * @return  void 
  146.      *
  147.      * @since   12.1
  148.      */
  149.     public function disconnect()
  150.     {
  151.         // Close the connection.
  152.         if (is_resource($this->connection))
  153.         {
  154.             foreach ($this->disconnectHandlers as $h)
  155.             {
  156.                 call_user_func_array($harray&$this));
  157.             }
  158.  
  159.             sqlsrv_close($this->connection);
  160.         }
  161.  
  162.         $this->connection = null;
  163.     }
  164.  
  165.     /**
  166.      * Get table constraints
  167.      *
  168.      * @param   string  $tableName  The name of the database table.
  169.      *
  170.      * @return  array  Any constraints available for the table.
  171.      *
  172.      * @since   12.1
  173.      */
  174.     protected function getTableConstraints($tableName)
  175.     {
  176.         $this->connect();
  177.  
  178.         $query $this->getQuery(true);
  179.  
  180.         $this->setQuery(
  181.             'SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = ' $query->quote($tableName)
  182.         );
  183.  
  184.         return $this->loadColumn();
  185.     }
  186.  
  187.     /**
  188.      * Rename constraints.
  189.      *
  190.      * @param   array   $constraints  Array(strings) of table constraints
  191.      * @param   string  $prefix       A string
  192.      * @param   string  $backup       A string
  193.      *
  194.      * @return  void 
  195.      *
  196.      * @since   12.1
  197.      */
  198.     protected function renameConstraints($constraints array()$prefix null$backup null)
  199.     {
  200.         $this->connect();
  201.  
  202.         foreach ($constraints as $constraint)
  203.         {
  204.             $this->setQuery('sp_rename ' $constraint ',' str_replace($prefix$backup$constraint));
  205.             $this->execute();
  206.         }
  207.     }
  208.  
  209.     /**
  210.      * Method to escape a string for usage in an SQL statement.
  211.      *
  212.      * The escaping for MSSQL isn't handled in the driver though that would be nice.  Because of this we need
  213.      * to handle the escaping ourselves.
  214.      *
  215.      * @param   string   $text   The string to be escaped.
  216.      * @param   boolean  $extra  Optional parameter to provide extra escaping.
  217.      *
  218.      * @return  string  The escaped string.
  219.      *
  220.      * @since   12.1
  221.      */
  222.     public function escape($text$extra false)
  223.     {
  224.         $result addslashes($text);
  225.         $result str_replace("\'""''"$result);
  226.         $result str_replace('\"''"'$result);
  227.         $result str_replace('\/''/'$result);
  228.  
  229.         if ($extra)
  230.         {
  231.             // We need the below str_replace since the search in sql server doesn't recognize _ character.
  232.             $result str_replace('_''[_]'$result);
  233.         }
  234.  
  235.         return $result;
  236.     }
  237.  
  238.     /**
  239.      * Determines if the connection to the server is active.
  240.      *
  241.      * @return  boolean  True if connected to the database engine.
  242.      *
  243.      * @since   12.1
  244.      */
  245.     public function connected()
  246.     {
  247.         // TODO: Run a blank query here
  248.         return true;
  249.     }
  250.  
  251.     /**
  252.      * Drops a table from the database.
  253.      *
  254.      * @param   string   $tableName  The name of the database table to drop.
  255.      * @param   boolean  $ifExists   Optionally specify that the table must exist before it is dropped.
  256.      *
  257.      * @return  JDatabaseDriverSqlsrv  Returns this object to support chaining.
  258.      *
  259.      * @since   12.1
  260.      */
  261.     public function dropTable($tableName$ifExists true)
  262.     {
  263.         $this->connect();
  264.  
  265.         $query $this->getQuery(true);
  266.  
  267.         if ($ifExists)
  268.         {
  269.             $this->setQuery(
  270.                 'IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ' $query->quote($tableName') DROP TABLE ' $tableName
  271.             );
  272.         }
  273.         else
  274.         {
  275.             $this->setQuery('DROP TABLE ' $tableName);
  276.         }
  277.  
  278.         $this->execute();
  279.  
  280.         return $this;
  281.     }
  282.  
  283.     /**
  284.      * Get the number of affected rows for the previous executed SQL statement.
  285.      *
  286.      * @return  integer  The number of affected rows.
  287.      *
  288.      * @since   12.1
  289.      */
  290.     public function getAffectedRows()
  291.     {
  292.         $this->connect();
  293.  
  294.         return sqlsrv_rows_affected($this->cursor);
  295.     }
  296.  
  297.     /**
  298.      * Method to get the database collation in use by sampling a text field of a table in the database.
  299.      *
  300.      * @return  mixed  The collation in use by the database or boolean false if not supported.
  301.      *
  302.      * @since   12.1
  303.      */
  304.     public function getCollation()
  305.     {
  306.         // TODO: Not fake this
  307.         return 'MSSQL UTF-8 (UCS2)';
  308.     }
  309.  
  310.     /**
  311.      * Get the number of returned rows for the previous executed SQL statement.
  312.      *
  313.      * @param   resource  $cursor  An optional database cursor resource to extract the row count from.
  314.      *
  315.      * @return  integer   The number of returned rows.
  316.      *
  317.      * @since   12.1
  318.      */
  319.     public function getNumRows($cursor null)
  320.     {
  321.         $this->connect();
  322.  
  323.         return sqlsrv_num_rows($cursor $cursor $this->cursor);
  324.     }
  325.  
  326.     /**
  327.      * Retrieves field information about the given tables.
  328.      *
  329.      * @param   mixed    $table     A table name
  330.      * @param   boolean  $typeOnly  True to only return field types.
  331.      *
  332.      * @return  array  An array of fields.
  333.      *
  334.      * @since   12.1
  335.      * @throws  RuntimeException
  336.      */
  337.     public function getTableColumns($table$typeOnly true)
  338.     {
  339.         $result array();
  340.  
  341.         $table_temp $this->replacePrefix((string) $table);
  342.  
  343.         // Set the query to get the table fields statement.
  344.         $this->setQuery(
  345.             'SELECT column_name as Field, data_type as Type, is_nullable as \'Null\', column_default as \'Default\'' .
  346.             ' FROM information_schema.columns WHERE table_name = ' $this->quote($table_temp)
  347.         );
  348.         $fields $this->loadObjectList();
  349.  
  350.         // If we only want the type as the value add just that to the list.
  351.         if ($typeOnly)
  352.         {
  353.             foreach ($fields as $field)
  354.             {
  355.                 $result[$field->Fieldpreg_replace("/[(0-9)]/"''$field->Type);
  356.             }
  357.         }
  358.         // If we want the whole field data object add that to the list.
  359.         else
  360.         {
  361.             foreach ($fields as $field)
  362.             {
  363.                 $result[$field->Field$field;
  364.             }
  365.         }
  366.  
  367.         return $result;
  368.     }
  369.  
  370.     /**
  371.      * Shows the table CREATE statement that creates the given tables.
  372.      *
  373.      * This is unsupported by MSSQL.
  374.      *
  375.      * @param   mixed  $tables  A table name or a list of table names.
  376.      *
  377.      * @return  array  A list of the create SQL for the tables.
  378.      *
  379.      * @since   12.1
  380.      * @throws  RuntimeException
  381.      */
  382.     public function getTableCreate($tables)
  383.     {
  384.         $this->connect();
  385.  
  386.         return '';
  387.     }
  388.  
  389.     /**
  390.      * Get the details list of keys for a table.
  391.      *
  392.      * @param   string  $table  The name of the table.
  393.      *
  394.      * @return  array  An array of the column specification for the table.
  395.      *
  396.      * @since   12.1
  397.      * @throws  RuntimeException
  398.      */
  399.     public function getTableKeys($table)
  400.     {
  401.         $this->connect();
  402.  
  403.         // TODO To implement.
  404.         return array();
  405.     }
  406.  
  407.     /**
  408.      * Method to get an array of all tables in the database.
  409.      *
  410.      * @return  array  An array of all the tables in the database.
  411.      *
  412.      * @since   12.1
  413.      * @throws  RuntimeException
  414.      */
  415.     public function getTableList()
  416.     {
  417.         $this->connect();
  418.  
  419.         // Set the query to get the tables statement.
  420.         $this->setQuery('SELECT name FROM ' $this->getDatabase('.sys.Tables WHERE type = \'U\';');
  421.         $tables $this->loadColumn();
  422.  
  423.         return $tables;
  424.     }
  425.  
  426.     /**
  427.      * Get the version of the database connector.
  428.      *
  429.      * @return  string  The database connector version.
  430.      *
  431.      * @since   12.1
  432.      */
  433.     public function getVersion()
  434.     {
  435.         $this->connect();
  436.  
  437.         $version sqlsrv_server_info($this->connection);
  438.  
  439.         return $version['SQLServerVersion'];
  440.     }
  441.  
  442.     /**
  443.      * Inserts a row into a table based on an object's properties.
  444.      *
  445.      * @param   string  $table    The name of the database table to insert into.
  446.      * @param   object  &$object  A reference to an object whose public properties match the table fields.
  447.      * @param   string  $key      The name of the primary key. If provided the object property is updated.
  448.      *
  449.      * @return  boolean    True on success.
  450.      *
  451.      * @since   12.1
  452.      * @throws  RuntimeException
  453.      */
  454.     public function insertObject($table&$object$key null)
  455.     {
  456.         $fields array();
  457.         $values array();
  458.         $statement 'INSERT INTO ' $this->quoteName($table' (%s) VALUES (%s)';
  459.  
  460.         foreach (get_object_vars($objectas $k => $v)
  461.         {
  462.             // Only process non-null scalars.
  463.             if (is_array($vor is_object($vor $v === null)
  464.             {
  465.                 continue;
  466.             }
  467.  
  468.             if (!$this->checkFieldExists($table$k))
  469.             {
  470.                 continue;
  471.             }
  472.  
  473.             if ($k[0== '_')
  474.             {
  475.                 // Internal field
  476.                 continue;
  477.             }
  478.  
  479.             if ($k == $key && $key == 0)
  480.             {
  481.                 continue;
  482.             }
  483.  
  484.             $fields[$this->quoteName($k);
  485.             $values[$this->Quote($v);
  486.         }
  487.         // Set the query and execute the insert.
  488.         $this->setQuery(sprintf($statementimplode(','$fields)implode(','$values)));
  489.  
  490.         if (!$this->execute())
  491.         {
  492.             return false;
  493.         }
  494.  
  495.         $id $this->insertid();
  496.  
  497.         if ($key && $id)
  498.         {
  499.             $object->$key $id;
  500.         }
  501.  
  502.         return true;
  503.     }
  504.  
  505.     /**
  506.      * Method to get the auto-incremented value from the last INSERT statement.
  507.      *
  508.      * @return  integer  The value of the auto-increment field from the last inserted row.
  509.      *
  510.      * @since   12.1
  511.      */
  512.     public function insertid()
  513.     {
  514.         $this->connect();
  515.  
  516.         // TODO: SELECT IDENTITY
  517.         $this->setQuery('SELECT @@IDENTITY');
  518.  
  519.         return (int) $this->loadResult();
  520.     }
  521.  
  522.     /**
  523.      * Method to get the first field of the first row of the result set from the database query.
  524.      *
  525.      * @return  mixed  The return value or null if the query failed.
  526.      *
  527.      * @since   12.1
  528.      * @throws  RuntimeException
  529.      */
  530.     public function loadResult()
  531.     {
  532.         $ret null;
  533.  
  534.         // Execute the query and get the result set cursor.
  535.         if (!($cursor $this->execute()))
  536.         {
  537.             return null;
  538.         }
  539.  
  540.         // Get the first row from the result set as an array.
  541.         if ($row sqlsrv_fetch_array($cursorSQLSRV_FETCH_NUMERIC))
  542.         {
  543.             $ret $row[0];
  544.         }
  545.  
  546.         // Free up system resources and return.
  547.         $this->freeResult($cursor);
  548.  
  549.         // For SQLServer - we need to strip slashes
  550.         $ret stripslashes($ret);
  551.  
  552.         return $ret;
  553.     }
  554.  
  555.     /**
  556.      * Execute the SQL statement.
  557.      *
  558.      * @return  mixed  A database cursor resource on success, boolean false on failure.
  559.      *
  560.      * @since   12.1
  561.      * @throws  RuntimeException
  562.      * @throws  Exception
  563.      */
  564.     public function execute()
  565.     {
  566.         $this->connect();
  567.  
  568.         if (!is_resource($this->connection))
  569.         {
  570.             JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED'$this->errorNum$this->errorMsg)JLog::ERROR'database');
  571.             throw new RuntimeException($this->errorMsg$this->errorNum);
  572.         }
  573.  
  574.         // Take a local copy so that we don't modify the original query and cause issues later
  575.         $query $this->replacePrefix((string) $this->sql);
  576.  
  577.         if (!($this->sql instanceof JDatabaseQuery&& ($this->limit > || $this->offset > 0))
  578.         {
  579.             $query $this->limit($query$this->limit$this->offset);
  580.         }
  581.  
  582.         // Increment the query counter.
  583.         $this->count++;
  584.  
  585.         // Reset the error values.
  586.         $this->errorNum = 0;
  587.         $this->errorMsg = '';
  588.  
  589.         // If debugging is enabled then let's log the query.
  590.         if ($this->debug)
  591.         {
  592.             // Add the query to the object queue.
  593.             $this->log[$query;
  594.  
  595.             JLog::add($queryJLog::DEBUG'databasequery');
  596.  
  597.             $this->timings[microtime(true);
  598.         }
  599.  
  600.         // SQLSrv_num_rows requires a static or keyset cursor.
  601.         if (strncmp(ltrim(strtoupper($query))'SELECT'strlen('SELECT')) == 0)
  602.         {
  603.             $array array('Scrollable' => SQLSRV_CURSOR_KEYSET);
  604.         }
  605.         else
  606.         {
  607.             $array array();
  608.         }
  609.  
  610.         // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost.
  611.         $this->cursor = @sqlsrv_query($this->connection$queryarray()$array);
  612.  
  613.         if ($this->debug)
  614.         {
  615.             $this->timings[microtime(true);
  616.             if (defined('DEBUG_BACKTRACE_IGNORE_ARGS'))
  617.             {
  618.                 $this->callStacks[debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
  619.             }
  620.             else
  621.             {
  622.                 $this->callStacks[debug_backtrace();
  623.             }
  624.         }
  625.  
  626.         // If an error occurred handle it.
  627.         if (!$this->cursor)
  628.         {
  629.             // Check if the server was disconnected.
  630.             if (!$this->connected())
  631.             {
  632.                 try
  633.                 {
  634.                     // Attempt to reconnect.
  635.                     $this->connection = null;
  636.                     $this->connect();
  637.                 }
  638.                 // If connect fails, ignore that exception and throw the normal exception.
  639.                 catch (RuntimeException $e)
  640.                 {
  641.                     // Get the error number and message.
  642.                     $errors sqlsrv_errors();
  643.                     $this->errorNum = $errors[0]['SQLSTATE'];
  644.                     $this->errorMsg = $errors[0]['message''SQL=' $query;
  645.  
  646.                     // Throw the normal query exception.
  647.                     JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED'$this->errorNum$this->errorMsg)JLog::ERROR'databasequery');
  648.                     throw new RuntimeException($this->errorMsg$this->errorNum);
  649.                 }
  650.  
  651.                 // Since we were able to reconnect, run the query again.
  652.                 return $this->execute();
  653.             }
  654.             // The server was not disconnected.
  655.             else
  656.             {
  657.                 // Get the error number and message.
  658.                 $errors sqlsrv_errors();
  659.                 $this->errorNum = $errors[0]['SQLSTATE'];
  660.                 $this->errorMsg = $errors[0]['message''SQL=' $query;
  661.  
  662.                 // Throw the normal query exception.
  663.                 JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED'$this->errorNum$this->errorMsg)JLog::ERROR'databasequery');
  664.                 throw new RuntimeException($this->errorMsg$this->errorNum);
  665.             }
  666.         }
  667.  
  668.         return $this->cursor;
  669.     }
  670.  
  671.     /**
  672.      * This function replaces a string identifier <var>$prefix</var> with the string held is the
  673.      * <var>tablePrefix</var> class variable.
  674.      *
  675.      * @param   string  $query   The SQL statement to prepare.
  676.      * @param   string  $prefix  The common table prefix.
  677.      *
  678.      * @return  string  The processed SQL statement.
  679.      *
  680.      * @since   12.1
  681.      */
  682.     public function replacePrefix($query$prefix '#__')
  683.     {
  684.         $startPos 0;
  685.         $literal '';
  686.  
  687.         $query trim($query);
  688.         $n strlen($query);
  689.  
  690.         while ($startPos $n)
  691.         {
  692.             $ip strpos($query$prefix$startPos);
  693.  
  694.             if ($ip === false)
  695.             {
  696.                 break;
  697.             }
  698.  
  699.             $j strpos($query"N'"$startPos);
  700.             $k strpos($query'"'$startPos);
  701.  
  702.             if (($k !== false&& (($k $j|| ($j === false)))
  703.             {
  704.                 $quoteChar '"';
  705.                 $j $k;
  706.             }
  707.             else
  708.             {
  709.                 $quoteChar "'";
  710.             }
  711.  
  712.             if ($j === false)
  713.             {
  714.                 $j $n;
  715.             }
  716.  
  717.             $literal .= str_replace($prefix$this->tablePrefixsubstr($query$startPos$j $startPos));
  718.             $startPos $j;
  719.  
  720.             $j $startPos 1;
  721.  
  722.             if ($j >= $n)
  723.             {
  724.                 break;
  725.             }
  726.  
  727.             // Quote comes first, find end of quote
  728.             while (true)
  729.             {
  730.                 $k strpos($query$quoteChar$j);
  731.                 $escaped false;
  732.  
  733.                 if ($k === false)
  734.                 {
  735.                     break;
  736.                 }
  737.  
  738.                 $l $k 1;
  739.  
  740.                 while ($l >= && $query{$l== '\\')
  741.                 {
  742.                     $l--;
  743.                     $escaped !$escaped;
  744.                 }
  745.  
  746.                 if ($escaped)
  747.                 {
  748.                     $j $k 1;
  749.                     continue;
  750.                 }
  751.  
  752.                 break;
  753.             }
  754.  
  755.             if ($k === false)
  756.             {
  757.                 // Error in the query - no end quote; ignore it
  758.                 break;
  759.             }
  760.  
  761.             $literal .= substr($query$startPos$k $startPos 1);
  762.             $startPos $k 1;
  763.         }
  764.  
  765.         if ($startPos $n)
  766.         {
  767.             $literal .= substr($query$startPos$n $startPos);
  768.         }
  769.  
  770.         return $literal;
  771.     }
  772.  
  773.     /**
  774.      * Select a database for use.
  775.      *
  776.      * @param   string  $database  The name of the database to select for use.
  777.      *
  778.      * @return  boolean  True if the database was successfully selected.
  779.      *
  780.      * @since   12.1
  781.      * @throws  RuntimeException
  782.      */
  783.     public function select($database)
  784.     {
  785.         $this->connect();
  786.  
  787.         if (!$database)
  788.         {
  789.             return false;
  790.         }
  791.  
  792.         if (!sqlsrv_query($this->connection'USE ' $databasenullarray('scrollable' => SQLSRV_CURSOR_STATIC)))
  793.         {
  794.             throw new RuntimeException('Could not connect to database');
  795.         }
  796.  
  797.         return true;
  798.     }
  799.  
  800.     /**
  801.      * Set the connection to use UTF-8 character encoding.
  802.      *
  803.      * @return  boolean  True on success.
  804.      *
  805.      * @since   12.1
  806.      */
  807.     public function setUTF()
  808.     {
  809.         // TODO: Remove this?
  810.     }
  811.  
  812.     /**
  813.      * Method to commit a transaction.
  814.      *
  815.      * @param   boolean  $toSavepoint  If true, commit to the last savepoint.
  816.      *
  817.      * @return  void 
  818.      *
  819.      * @since   12.1
  820.      * @throws  RuntimeException
  821.      */
  822.     public function transactionCommit($toSavepoint false)
  823.     {
  824.         $this->connect();
  825.  
  826.         if (!$toSavepoint || $this->transactionDepth <= 1)
  827.         {
  828.             if ($this->setQuery('COMMIT TRANSACTION')->execute())
  829.             {
  830.                 $this->transactionDepth = 0;
  831.             }
  832.  
  833.             return;
  834.         }
  835.  
  836.         $this->transactionDepth--;
  837.     }
  838.  
  839.     /**
  840.      * Method to roll back a transaction.
  841.      *
  842.      * @param   boolean  $toSavepoint  If true, rollback to the last savepoint.
  843.      *
  844.      * @return  void 
  845.      *
  846.      * @since   12.1
  847.      * @throws  RuntimeException
  848.      */
  849.     public function transactionRollback($toSavepoint false)
  850.     {
  851.         $this->connect();
  852.  
  853.         if (!$toSavepoint || $this->transactionDepth <= 1)
  854.         {
  855.             if ($this->setQuery('ROLLBACK TRANSACTION')->execute())
  856.             {
  857.                 $this->transactionDepth = 0;
  858.             }
  859.  
  860.             return;
  861.         }
  862.  
  863.         $savepoint 'SP_' ($this->transactionDepth - 1);
  864.         $this->setQuery('ROLLBACK TRANSACTION ' $this->quoteName($savepoint));
  865.  
  866.         if ($this->execute())
  867.         {
  868.             $this->transactionDepth--;
  869.         }
  870.     }
  871.  
  872.     /**
  873.      * Method to initialize a transaction.
  874.      *
  875.      * @param   boolean  $asSavepoint  If true and a transaction is already active, a savepoint will be created.
  876.      *
  877.      * @return  void 
  878.      *
  879.      * @since   12.1
  880.      * @throws  RuntimeException
  881.      */
  882.     public function transactionStart($asSavepoint false)
  883.     {
  884.         $this->connect();
  885.  
  886.         if (!$asSavepoint || !$this->transactionDepth)
  887.         {
  888.             if ($this->setQuery('BEGIN TRANSACTION')->execute())
  889.             {
  890.                 $this->transactionDepth = 1;
  891.             }
  892.  
  893.             return;
  894.         }
  895.  
  896.         $savepoint 'SP_' $this->transactionDepth;
  897.         $this->setQuery('BEGIN TRANSACTION ' $this->quoteName($savepoint));
  898.  
  899.         if ($this->execute())
  900.         {
  901.             $this->transactionDepth++;
  902.         }
  903.     }
  904.  
  905.     /**
  906.      * Method to fetch a row from the result set cursor as an array.
  907.      *
  908.      * @param   mixed  $cursor  The optional result set cursor from which to fetch the row.
  909.      *
  910.      * @return  mixed  Either the next row from the result set or false if there are no more rows.
  911.      *
  912.      * @since   12.1
  913.      */
  914.     protected function fetchArray($cursor null)
  915.     {
  916.         return sqlsrv_fetch_array($cursor $cursor $this->cursorSQLSRV_FETCH_NUMERIC);
  917.     }
  918.  
  919.     /**
  920.      * Method to fetch a row from the result set cursor as an associative array.
  921.      *
  922.      * @param   mixed  $cursor  The optional result set cursor from which to fetch the row.
  923.      *
  924.      * @return  mixed  Either the next row from the result set or false if there are no more rows.
  925.      *
  926.      * @since   12.1
  927.      */
  928.     protected function fetchAssoc($cursor null)
  929.     {
  930.         return sqlsrv_fetch_array($cursor $cursor $this->cursorSQLSRV_FETCH_ASSOC);
  931.     }
  932.  
  933.     /**
  934.      * Method to fetch a row from the result set cursor as an object.
  935.      *
  936.      * @param   mixed   $cursor  The optional result set cursor from which to fetch the row.
  937.      * @param   string  $class   The class name to use for the returned row object.
  938.      *
  939.      * @return  mixed   Either the next row from the result set or false if there are no more rows.
  940.      *
  941.      * @since   12.1
  942.      */
  943.     protected function fetchObject($cursor null$class 'stdClass')
  944.     {
  945.         return sqlsrv_fetch_object($cursor $cursor $this->cursor$class);
  946.     }
  947.  
  948.     /**
  949.      * Method to free up the memory used for the result set.
  950.      *
  951.      * @param   mixed  $cursor  The optional result set cursor from which to fetch the row.
  952.      *
  953.      * @return  void 
  954.      *
  955.      * @since   12.1
  956.      */
  957.     protected function freeResult($cursor null)
  958.     {
  959.         sqlsrv_free_stmt($cursor $cursor $this->cursor);
  960.     }
  961.  
  962.     /**
  963.      * Method to check and see if a field exists in a table.
  964.      *
  965.      * @param   string  $table  The table in which to verify the field.
  966.      * @param   string  $field  The field to verify.
  967.      *
  968.      * @return  boolean  True if the field exists in the table.
  969.      *
  970.      * @since   12.1
  971.      */
  972.     protected function checkFieldExists($table$field)
  973.     {
  974.         $this->connect();
  975.  
  976.         $table $this->replacePrefix((string) $table);
  977.         $query "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '$table' AND COLUMN_NAME = '$field'.
  978.             " ORDER BY ORDINAL_POSITION";
  979.         $this->setQuery($query);
  980.  
  981.         if ($this->loadResult())
  982.         {
  983.             return true;
  984.         }
  985.         else
  986.         {
  987.             return false;
  988.         }
  989.     }
  990.  
  991.     /**
  992.      * Method to wrap an SQL statement to provide a LIMIT and OFFSET behavior for scrolling through a result set.
  993.      *
  994.      * @param   string   $query   The SQL statement to process.
  995.      * @param   integer  $limit   The maximum affected rows to set.
  996.      * @param   integer  $offset  The affected row offset to set.
  997.      *
  998.      * @return  string   The processed SQL statement.
  999.      *
  1000.      * @since   12.1
  1001.      */
  1002.     protected function limit($query$limit$offset)
  1003.     {
  1004.         if ($limit == && $offset == 0)
  1005.         {
  1006.             return $query;
  1007.         }
  1008.  
  1009.         $start $offset 1;
  1010.         $end   $offset $limit;
  1011.  
  1012.         $orderBy stristr($query'ORDER BY');
  1013.  
  1014.         if (is_null($orderBy|| empty($orderBy))
  1015.         {
  1016.             $orderBy 'ORDER BY (select 0)';
  1017.         }
  1018.  
  1019.         $query str_ireplace($orderBy''$query);
  1020.  
  1021.         $rowNumberText ', ROW_NUMBER() OVER (' $orderBy ') AS RowNumber FROM ';
  1022.  
  1023.         $query preg_replace('/\sFROM\s/i'$rowNumberText$query1);
  1024.         $query 'SELECT * FROM (' $query ') _myResults WHERE RowNumber BETWEEN ' $start ' AND ' $end;
  1025.  
  1026.         return $query;
  1027.     }
  1028.  
  1029.     /**
  1030.      * Renames a table in the database.
  1031.      *
  1032.      * @param   string  $oldTable  The name of the table to be renamed
  1033.      * @param   string  $newTable  The new name for the table.
  1034.      * @param   string  $backup    Table prefix
  1035.      * @param   string  $prefix    For the table - used to rename constraints in non-mysql databases
  1036.      *
  1037.      * @return  JDatabaseDriverSqlsrv  Returns this object to support chaining.
  1038.      *
  1039.      * @since   12.1
  1040.      * @throws  RuntimeException
  1041.      */
  1042.     public function renameTable($oldTable$newTable$backup null$prefix null)
  1043.     {
  1044.         $constraints array();
  1045.  
  1046.         if (!is_null($prefix&& !is_null($backup))
  1047.         {
  1048.             $constraints $this->getTableConstraints($oldTable);
  1049.         }
  1050.         if (!empty($constraints))
  1051.         {
  1052.             $this->renameConstraints($constraints$prefix$backup);
  1053.         }
  1054.  
  1055.         $this->setQuery("sp_rename '" $oldTable "', '" $newTable "'");
  1056.  
  1057.         return $this->execute();
  1058.     }
  1059.  
  1060.     /**
  1061.      * Locks a table in the database.
  1062.      *
  1063.      * @param   string  $tableName  The name of the table to lock.
  1064.      *
  1065.      * @return  JDatabaseDriverSqlsrv  Returns this object to support chaining.
  1066.      *
  1067.      * @since   12.1
  1068.      * @throws  RuntimeException
  1069.      */
  1070.     public function lockTable($tableName)
  1071.     {
  1072.         return $this;
  1073.     }
  1074.  
  1075.     /**
  1076.      * Unlocks tables in the database.
  1077.      *
  1078.      * @return  JDatabaseDriverSqlsrv  Returns this object to support chaining.
  1079.      *
  1080.      * @since   12.1
  1081.      * @throws  RuntimeException
  1082.      */
  1083.     public function unlockTables()
  1084.     {
  1085.         return $this;
  1086.     }
  1087. }

Documentation generated on Tue, 19 Nov 2013 15:14:14 +0100 by phpDocumentor 1.4.3