Welcome to MSDN Blogs Sign in | Join | Help
The survey is about to close...

Have you submitted your response to the survey yet?  

If you have,  thank you for taking the time to provide us with your feedback.

If you have not yet taken the survey OR have not finished it yet, it's time to get it completed so that your voice can be heard.  

The survey, which is available here will close on 21st October 2009.  

Thank you,

Raghu Ram

Principal Group Program Manager,

SQL Connectivity,

Microsoft Corp,

Redmond, WA

 

SQL Server Driver for PHP 1.1 is now available

It is my pleasure to announce that the Microsoft SQL Server Driver for PHP 1.1 has released! 

 

The SQL Server Driver for PHP team would like to thank everyone who has provided feedback and bug reports throughout the development cycle for the driver. 

 

This release marks a very big milestone in our continued pursuit for interoperability while also providing support for PHP ver 5.3.   This driver will enable developers to build PHP applications with relational database capabilities to both SQL Server as well as SQL Azure databases.   There are some key performance improvements as well as new features such as support for UTF-8 encoding and scrollable result sets.   

 

Downloads

 

The driver is available for download on the MSDN download site.  We encourage you to download the driver and explore the sample application described in the .chm file.  If you'd prefer to access our documentation on-line, it's available on the MSDN site.  We welcome your feedback, feature requests and bug reports on our MSDN Forum.

 

What about the source?

 

Microsoft has published the source code to the driver on the CodePlex site.  There, you can download the source code and provide feedback in the form of bug reports and feature requests.

 

We understand that many developers will download the source code from CodePlex to create their own build(s) of the driver.  However, Microsoft supports only the signed version of the driver from the MSDN download site.  If you're using a custom built version of the driver and encounter problems, please reproduce the problem with the Microsoft-built driver before contacting Microsoft support.

 

How will Microsoft respond to feedback and update the driver?

We will be responsive to feedback on the MSDN Forum, weighing in on both feature requests and bug reports.

 

Thanks, 

Raghu Ram 

Principal Group Program Manager

SQL Connectivity

Microsoft Corp

Redmond, WA

 

We would like to hear your opinion....

Dear SQL Server developers and users:

It is this time of the year when we in the SQL Connectivity product team take a step back, review the priorities and goals for the long term and identify areas that will benefit from investments.

We view YOU and your organization as a key stakeholder in this process and would like to gather your inputs in this survey, which should take no more than 5 - 10 minutes and a few other surveys that we will conduct in the next few months. The feedback you provide is very valuable and be rest assured that each and every response will be read and will provide the background for some of the key decisions that we will make that will benefit our user community - developers, DBAs and all those who use SQL Server or are looking for ways to expand the scope of the power of SQL Server within your organizations.

 

This survey will be open for your submissions until October 21, 2009 and is available here 

Thank you,

Raghu Ram

Principal Group Program Manager,

SQL Connectivity,

Microsoft Corp,

Redmond, WA

SQL Server Driver for PHP 1.1 Community Technology Preview is now available

Today we are excited to announce that the Community Technology Preview (CTP) of the Microsoft® SQL Server Driver for PHP 1.1.   This version of the driver provides new capabilities for building PHP applications and support for SQL Azure as well as support for PHP version 5.3, enabling developers to build PHP applications with relational capabilities using SQL Azure Database.    Details of the SQL Azure Database CTP is available in a blogpost here.

 SQL Server Driver for PHP 1.1 CTP will include:

·         Support for SQL Azure

·         Support for PHP version 5.3

·         Support for UTF-8

·         Support for Scrollable results, row countMigration to SQL Server 2008 Native Client framework that provides for performance enhancements.

You can download the CTP of the SQL Server Driver for PHP 1.1 here.

The SQL Connectivity PHP team would welcome your feedback on this release.  

Thanks,

Raghu Ram

Principal Group Program Manager

SQL Connectivity

 

The SQL Server Driver for PHP is now available in the Web Platform Installer 2.0 beta

For those who are not already aware of the Web Platform Installer (WPI), it's a great tool for setting up a Windows machine for Web app development and deployment.  You can download the beta of WPI 2.0 or the WPI 1.0 release here.

 

PHP itself is available via the WPI 2.0 beta.  Installing PHP through the WPI 2.0 beta also automatically configures FastCGI as part of the installation.  Mai-lan from the API team has a great blog post here with more information about installing PHP via WPI.

 

I'm pleased to announce that the SQL Server Driver for PHP is also now available in the Web Platform Installer 2.0 beta.  Enjoy!

 

SQL Server Driver for PHP in Web Platform Installer 2.0 beta

 

David Sceppa

Program Manager - Microsoft SQL Server Driver for PHP

 

April 2009 Cumulative Update Now Available!

It is my pleasure to announce that Microsoft has released an update to version 1.0 of the Microsoft SQL Server Driver for PHP.  The update to the driver is the April 2009 Cumulative Update and it contains a series of targeted fixes, the majority of which were reported by the community via the MSDN Forum.  For more details on the specific fixes, please see the readme included in the download package.

 

There are two non-technical fixes we've made in the update worth noting.

  1. The extension is now the Microsoft SQL Server Driver for PHP, version 1.0.  We'd received a number of questions about whether or not the extension supported SQL Server 2008 because the extension had been named the SQL Server 2005 Driver for PHP.  The extension supports both SQL Server 2008 and SQL Server 2005, including all editions from Express to Enterprise.
  2. We've improved the error message returned when the ODBC driver that the extension relies on is not installed.  The error message now calls out that the cause of the error and includes the URL that developers can use to download and install the ODBC driver.

The updated binary version of the extension is available for download on the MSDN download site and replaces the v1.0 RTM release.

 

We have also published the updates to the source on the CodePlex site.  We've modified the source code specifically so that the extension can be compiled and used with the current builds of PHP 5.3.

 

Enjoy!

 

David Sceppa

Program Manager - Microsoft SQL Server Driver for PHP

Paging Data with the SQL Server Driver for PHP

One of the most common tasks of any web application involves allowing a user to ‘page’ through a large amount of data. In other words, given a large result set, the task is to present the user with a manageable subset and allow him/her to navigate through the result set by viewing ‘pages’ (i.e. subsets) of data. In this post, I’ll demonstrate two ways to do this with the existing functionality of the SQL Server Driver for PHP. Also note that the strategies discussed here can be applied to paging data regardless of the programming language or database.

 

Before diving into the details of the two examples, I want to point out two assumptions I have made:

1)      Data on the server is dynamic (i.e. data may be updated between our query executions).

2)      We want the most recent data with each page view.

With these assumptions in mind, the examples below demonstrate two similar strategies for paging. If your paging needs do not share these assumptions, you may want to consider another strategy (such as executing queries against an ordered temporary table or bringing a complete result set into client memory) that meets your needs.

 

The first example allows users to navigate through data with “Previous Page” and “Next Page” links, while the second example allows users to navigate directly to any page.

 

Note that both examples use the AdventureWorks database and that I’ve removed some of the connection code and error handling for the sake of brevity. Complete scripts are attached to this post if you want to see the code in-tact.

 

“Previous Page” and “Next Page” Navigation

This example presents users with ten products at a time and allows him/her to navigate through pages with “Previous Page” and “Next Page” links. The basic strategy is to retrieve an “extra” row (an 11th row in my example) and use it as a flag for enabling the “Next Page” link. This avoids multiple trips to the database to display one page of data.

This first section of code sets the number of rows to display on a page and executes a query that returns and “extra” row.  The query uses the ROW_NUMBER() function and OVER clause in the context of a common table expression to produce an efficient query. Common table expressions were added to T-SQL in SQL Server 2005 and are extremely helpful for paging scenarios such as this, along with scenarios where you want to execute recursive queries or want to reference a complex result multiple times within a single statement. The code for connecting to the database has been removed and the code assumes “lowRowNum” and “highRowNum” values are being passed in the $_REQUEST variable:

/* Set the number of results to display on each page. */

$rowsPerPage = 10;

 

/* Order target data by ID and select only items

   (by row number) to display on a given page.

   The query asks for one "extra" row as an

   indicator that another page of data exists. */

$tsql = "WITH Products AS

            (SELECT ROW_NUMBER() OVER(ORDER BY ProductID) AS

                  RowNumber,

                  Name,

                  ProductNumber

       FROM Production.Product)

         SELECT * FROM Products

         WHERE RowNumber BETWEEN ? AND ? + 1";

 

/* Determine which row numbers to display. */

if(isset($_REQUEST['lowRowNum']) &&

   isset($_REQUEST['highRowNum']))

{

      $lowRowNum = $_REQUEST['lowRowNum'];

      $highRowNum = $_REQUEST['highRowNum'];

}

else

{

      $lowRowNum = 1;

      $highRowNum = $rowsPerPage;

}

 

/* Set query parameter values. */

$params = array($lowRowNum, $highRowNum);

 

/* Execute the query. */

$stmt = sqlsrv_query($conn, $tsql, $params); 

 

Next, we need to display the retrieved data - if there is any! This code retrieves one row of data to make sure some data was returned. (We are working on features in v1.1 of the driver that will make this easier.) Then we display a row of data at a time until we have displayed $rowsPerPage rows, or until there are no more rows to display. If we have displayed $rowsPerPage rows and there is another row of data (that “extra” row), we display the “Next Page” link:

 

/* Retrieve one row to see if there is any data. */

$row = sqlsrv_fetch_array($stmt);

if($row === false)

{

      echo "Error in fetching row.";

     die( print_r( sqlsrv_errors(), true));

}

elseif($row[0] == 0) /* Special case of no data returned. */

{

      echo "No data returned.";

}

else /* A row was retrieved! */

{

   /* Set the number of rows that have been retrieved. */

   $rowsRetrieved = 1;

 

   /*Display table header. */

   print("<table border='1px'>

                <tr>

                   <td>Row Number</td>

                   <td>Product Name</td>

                   <td>Product ID</td>

                </tr>");

 

   /* Display the retrieved rows while we haven't

      displayed all of $rowsPerPage and there is

      another row to display. */

   do

   {

         print("<tr>

                    <td>$row[0]</td>

                    <td>$row[1]</td>

                    <td>$row[2]</td>

                    </tr>");

         $rowsRetrieved++;

     

   } while ($rowsRetrieved <= $rowsPerPage &

            $row = sqlsrv_fetch_array($stmt));

     

   /* Close table. */

   print("</table></br></br>");

 

   /*If there are previous results, display the

     Previous Page link.*/

   if($lowRowNum > 1)

   {

      $prev_page_high = $lowRowNum;

      $prev_page_low = $lowRowNum - $rowsPerPage;

      $prevPage = "?lowRowNum=$prev_page_low".

                  "&highRowNum=$prev_page_high";

      print("<a href=$prevPage>".

            "Previous Page</a>&nbsp;&nbsp;&nbsp;");

   }

 

   /* If there are more results, display the Next Page link.

      We know there are more results if the last call to

      sqlsrv_fetch_array returned a row (the "extra" row).

   */

   if($row != false)

   {       

      $next_page_low = $highRowNum;

      $next_page_high = $highRowNum + $rowsPerPage;

      $nextPage = "?lowRowNum=$next_page_low".

                  "&highRowNum=$next_page_high";

      print("<a href=$nextPage>Next Page</a>");

   }    

} 

 

Direct Page Navigation

The next example presents the user with a series of page links that allows the user to click to any page of the results he/she wants to see (without having to click “Next Page” or “Previous Page” several times).  This strategy requires executing two queries: one to retrieve the number of rows that are available, and one to actually retrieve the rows that will be displayed.

 

The following code executes a query to see how many rows of data are on the server, then populates the page with links that will query for $rowsPerPage at a time. Note that this query is executed every time the page is loaded, which is desirable if the data on the server is changing frequently. Also note that the query leverages the SQL Server COUNT function to query for the number of rows that are available without actually querying for the row data.

  

/* Set the number of results to display on each page. */

$rowsPerPage = 10;

  

/* Define a query to get the number of rows on the server.

   This query doesn't actually retrieve rows, it just

   retrieves the number of rows that are available. */

$tsql = "SELECT COUNT(ProductNumber)

             FROM Production.Product";

 

/* Execute the query. */

$stmt = sqlsrv_query($conn, $tsql);

if($stmt === false)

{

     echo "Error in query execution.";

     die( print_r( sqlsrv_errors(), true));

}

 

/* Get the number of rows returned. */

$rowsReturned = sqlsrv_fetch_array($stmt);

if($rowsReturned === false)

{

     echo "Error in retrieving number of rows.";

     die( print_r( sqlsrv_errors(), true));

}

elseif($rowsReturned[0] == 0)

{

      echo "No rows returned.";

}

else

{    

      /* Display page links. */

      $numOfPages = ceil($rowsReturned[0]/$rowsPerPage);

      for($i = 1; $i<=$numOfPages; $i++)

      {

            $pageNum = "?pageNum=$i";

            print("<a href=$pageNum>$i</a>&nbsp;&nbsp;");

      }

      echo "</br></br>";

} 

 

 Now we need to retrieve and display the data. We’ll use the same query as in the first example to retrieve data. The only tricky part here is in determining what the query parameters are. I’m determining the $highRowNum value by multiplying the page clicked by the $rowsPerPage  (25, in my case). The $lowRowNum value is determined by subtracting $resultsPerPage from $highRowNum. (I suppose I could have included this logic in the code above when printing the series of links to pages, but one way doesn’t seem to have significant advantages over the other.)

 

 

/* Order target data by ID and select only items

   (by row number) to display on a given page. */

$tsql = "WITH Products AS

            (SELECT ROW_NUMBER() OVER(ORDER BY ProductID)

                         AS RowNumber,

                            Name,    

                            ProductNumber

             FROM Production.Product)

          SELECT * FROM Products

          WHERE RowNumber BETWEEN ? AND ?";

 

/* Determine which row numbers to display. */

if(isset($_REQUEST['pageNum']))

{

      $highRowNum = $_REQUEST['pageNum'] * $rowsPerPage;

      $lowRowNum = $highRowNum - $rowsPerPage;

}

else

{

      $lowRowNum = 1;

      $highRowNum = $rowsPerPage;

}

 

/* Set query parameter values. */

$params = array($lowRowNum, $highRowNum);

 

/* Execute the query. */

$stmt2 = sqlsrv_query($conn, $tsql, $params);

if($stmt2 === false)

{

     echo "Error in query execution.";

     die( print_r( sqlsrv_errors(), true));

}

 

/* Print table header. */

print("<table border='1px'>

           <tr>

              <td>Row Number</td>

              <td>Product Name</td>

              <td>Product ID</td>

           </tr>");

 

/* Display results. */

while($row = sqlsrv_fetch_array($stmt2) )

{

      print("<tr>

                 <td>$row[0]</td>

                 <td>$row[1]</td>

                 <td>$row[2]</td>

             </tr>");

}

 

/* Close table. */

print("</table>"); 

 

And that’s it! Two similar strategies for paging with different UI’s. I hope this has been a useful post – please let us know what you think. For more information about the SQL Server Driver for PHP, see the product documentation.

 

Thanks.

Brian Swan

Programming Writer, SQL Server Driver for PHP

 

Working on version 1.1 of the SQL Server Driver for PHP

The SQL Server Driver for PHP team is hard at work on the version 1.1 of the extension.  We've received a lot of feedback for feature requests from PHP developers through the blog, forums, the Seattle PHP meetup group, and various conferences.  The most frequent feature requests have been in the following areas: 

  1. Support for PHP 5.3
  2. Support for handling UTF-8 strings in parameters and resultset values, without requiring calls to iconv or other extensions
  3. Support for scrollable results and row count

We are working on addressing these feature requests for the next version of the extension.

 

We are doing some preliminary work to test our existing version 1.0 extension with PHP 5.3 and providing guidance to help developers working with current builds of PHP 5.3 use the version 1.0 extension.  Keep an eye on this blog for more information regarding using the version 1.0 driver with PHP 5.3.  We expect to fully support PHP 5.3 with version 1.1 of our extension.

 

For UTF-8 strings, we are working on adding support for this and other character sets to the extension.  Having the SQL Server Driver for PHP handle UTF-8 strings means that the extension will handle the translations between UTF-8 and UCS-2 (little endian) since the SQL Server database engine does not currently support the UTF-8 character set.  We're working on handling parameters and resultset values using UTF-8, as well as handling the query strings in UTF-8 format.

 

We are also working on adding scrollable resultset support to the extension.  The initial version of the SQL Server Driver for PHP retrieves query results in an unbuffered forward-only mode, an approach that yields the best possible performance for retrieving query results from SQL Server but one where there is no way to know how many rows the resultset contains ahead of time.  We are planning on adding support for scrollable resultsets to the extension in version 1.1, giving PHP developers the ability to access any row as well as the ability to determine how many rows are available within the resultset.

 

David Sceppa

Program Manager - Microsoft SQL Server Driver for PHP

Using the FILESTREAM Functionality of SQL Server 2008

In this post I want to demonstrate how the streaming capabilities of the SQL Server 2005 Driver for PHP work seamlessly with the new FILESTREAM functionality in SQL Server 2008.

The FILESTREAM functionality in SQL Server 2008 enables applications to store unstructured data, such as documents and images, on the file system. Applications can leverage the rich streaming APIs and performance of the file system and at the same time maintain transactional consistency between the unstructured data and corresponding structured data. You can learn more about the FILESTREAM feature by starting here: Designing and Implementing FILESTREAM Storage.

To follow this demonstration, you will need to do two things first:

1)      Enable FILESTREAM on an instance of SQL Server. Instructions for doing this are here: How to: Enable FILESTREAM.

2)      Create a FILESTREAM-enabled database. Instructions for doing this are here: How to: Create a FILESTREAM-Enabled Database. The database I will use for demonstration purposes can be created with this script:

USE [master];
GO
 
IF EXISTS (SELECT * FROM sys.databases
           WHERE name = 'FilestreamDB')
    DROP DATABASE FilestreamDB;
GO
 
CREATE DATABASE FilestreamDB
ON
PRIMARY (NAME = F_Stream1, FILENAME = 'c:\data\FilestreamDB.mdf'),
    FILEGROUP FileStreamGroup1 CONTAINS

         FILESTREAM(NAME = F_Stream2, FILENAME = 'c:\data\filestream1')
    LOG ON (NAME = F_StreamLog1, FILENAME = 'c:\data\FilestreamDB_log.ldf')
GO
 
USE FilestreamDB;
GO
 
Create Table StreamTable (
     ID uniqueidentifier rowguidcol NOT NULL unique,
     myFileStream varbinary(max) FILESTREAM
)

 

Once you have completed these two steps, the coding patterns for sending and retrieving stream data with the SQL Server 2005 Driver for PHP are no different than those for sending/retrieving stream data from other column types (see How to: Send Stream Data and Retrieving Stream Data for more information).

Let’s look at an example of sending and retrieving FILESTREAM data.

Sending FILESTREAM Data

Although the code pattern for sending stream data to a FILESTREAM column is the same as it is for sending stream data to any column, the Transact-SQL that defines the query may new. A FILESTREAM-enabled database requires a uniqueidentifier column, and there is no function (such as SCOPE_IDENTITY()) to retrieve the generated GUID after performing an INSERT. So, in the code below, I’ve modified the Transact-SQL so I can select the GUID for use later. Also note that, in the parameter array, I’ve specified the PHP type as a binary stream and the SQL type as varbinary(‘max’) (see How to: Specify PHP Data Types and How to: Specify SQL Server Data Types for more information).

/* Define a parameterized query to insert data. */
$tsql = "DECLARE @id UNIQUEIDENTIFIER;
         SET @id = NEWID();
         INSERT INTO StreamTable (ID, myFileStream)

             VALUES (@id, ?);
         SELECT @id AS ID";
 
/* Open data as a stream. */
$pic = fopen("C:\Pics\Tree.jpg","r");
 
/* Define the parameter array. */
/* Note the specification of the PHPTYPE and SQLTYPE. */
$params = array(
           array(
                $pic,
                 SQLSRV_PARAM_IN,
                 SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY),
                 SQLSRV_SQLTYPE_VARBINARY('max')
                )
           );
 
/* Execute the query. */
$stmt = sqlsrv_query($conn, $tsql, $params);
if ($stmt === false) {
     echo "Error in statement execution.</br>";
     die( print_r( sqlsrv_errors(), true));
}

 

Retrieving FILESTREAM Data

The coding pattern for retrieving FILESTREAM data is exactly as it is for retrieving any type of streamable data. In the code below, I have to do a little work to retrieve the GUID by moving to the next result set of the query executed in the code above.

/* Skip the rows affected result by moving to the next result. */
if(sqlsrv_next_result($stmt) === false)
{
     echo "Error in moving to next result.</br>";
     die( print_r( sqlsrv_errors(), true));
}
 
/* Retrieve the first (only) row of the next result. */
if(sqlsrv_fetch($stmt) === false)
{
     echo "Error in retrieving row.</br>";
     die( print_r( sqlsrv_errors(), true));
}
 
/* Get the first field of the row and assign the value to $id. */
if( !($id = sqlsrv_get_field($stmt, 0)) )
{
     echo "Error in retrieving field.</br>";
     die( print_r( sqlsrv_errors(), true));
}
 
/* Define a parameterized query to retrieve the newly inserted data. */
$tsql = "SELECT myFileStream FROM StreamTable WHERE ID = ?";
 
/* Use the retrieved uniqueidentifier as the parameter value. */
$params = array($id);
 
/* Execute the query. */
$stmt = sqlsrv_query($conn, $tsql, $params);
if( $stmt === false )
{
     echo "Error in statement execution.</br>";
     die( print_r( sqlsrv_errors(), true));
}
 
/* Retrieve the results as a binary stream and display in the browser. */
if ( sqlsrv_fetch( $stmt ) )
{
   $image = sqlsrv_get_field($stmt, 0,

                             SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY));
   fpassthru($image);
}
else
{
     echo "Error in retrieving data.</br>";
     die(print_r( sqlsrv_errors(), true));
}

 

That’s it! There are many advantages to using the FILESTREAM functionality of SQL Server 2008, and it is easy to leverage this functionality with the streaming capabilities of the the SQL Server 2005 Driver for PHP.

Thanks.

Brian Swan

Programming Writer, SQL Server 2005 Driver for PHP

Working with the New SQL Server 2008 Data Types

One question we have heard a lot since releasing the SQL Server 2005 Driver for PHP is “Does the driver work with SQL Server 2008?” The short answer is “Yes!” However, because the driver relies on the SQL Server 2005 ODBC driver to handle low-level communications with the server, we have heard some follow up questions about how our driver works with the new data types that are available in SQL Server 2008. In this post, I will demonstrate how the SQL Server 2005 Driver for PHP interacts with these new data types. If you are not familiar with the new SQL Server 2008 data types, you can read an overview here: SQL Server 2008: New Data Types.

 

New Data Types Summary

Here’s a quick summary of the new data types in SQL Server 2008 (with links to more detailed explanations):

·         date: A date.

·         time: A time.

·         datetimeoffset : A date that is combined with a time of a day that has time zone awareness and is based on a 24-hour clock.

·         datetime2 : A date that is combined with a time of day that is based on 24-hour clock. This type that has a larger date range, a larger default fractional precision than the legacy datetime type, and it allows for optional user-specified precision.

·         hierarchyid : A variable length, system data type used to represent position in a hierarchy.

·         geometry : A .NET Common Language Runtime (CLR) data type that represents data in a Euclidean (flat) coordinate system.

·         geography : A .NET common language runtime (CLR) data type that represents data in a round-earth coordinate system.

 

Retrieving New Data Types

You can retrieve any of the types mentioned above as strings. With the date, time, datetimeoffset, and datetime2 types, this happens by default when you retrieve these types with sqlsrv_fetch_array or the combination of sqlsrv_fetch/sqlsrv_get_field.  Note that the legacy datetime and smalldatetime types are returned as  PHP Datetime objects by default. We’d be interested in your feedback about the preferred default return types for the new data types, as well as the legacy types.

The process for retrieving the hierarchyid, geometry, and geography types as strings has a couple of twists, so let’s take a look at some examples. All the examples below are run against the NewTypesDB database; a SQL script that creates and populates this database is attached to this post. Also attached is a PHP script that contains the examples shown here, plus examples of how to retrieve the date, time, datetimeoffset, and datetime2 types.

To retrieve the geometry and geography types as strings, the .ToString() method must be called on each type in the Transact-SQL that defines the query.  This method converts the data to the SQL Server nvarchar(max) data type, which the SQL Server 2005 Driver for PHP converts to a PHP stream by default.  So, one option is to retrieve these types with sqlsrv_fetch_array, which will convert the streams to strings automatically:

Note: SQL Server does not return a column name by default when the .ToString() method is called on a column. In the query below, I have specified column names in the Transact-SQL.

$tsql = "SELECT myGeometry.ToString()AS geom,
         myGeography.ToString() AS geog
         FROM GeoTable";
$stmt = sqlsrv_query($conn, $tsql);
 
while($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))
{
     print("<pre>");
     print_r($row);
     print("</pre>");
}

 

  

Another option would be to retrieve these types with the combination of sqlsrv_fetch/sqlsrv_get_field and specify the PHP return type as a string:

$tsql = "SELECT myGeometry.ToString()AS geom,
         myGeography.ToString() AS geog
         FROM GeoTable";
$stmt = sqlsrv_query($conn, $tsql);
 
while(sqlsrv_fetch($stmt))
{
     echo sqlsrv_get_field($stmt, 0,

          SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR))."</br>";
     echo sqlsrv_get_field($stmt, 1,

          SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR))."</br>";
}

 

  

And, finally, a third option would be to retrieve these types as streams (the default):

$tsql = "SELECT myGeometry.ToString()AS geom,
         myGeography.ToString() AS geog
         FROM GeoTable";
$stmt = sqlsrv_query($conn, $tsql);

while(sqlsrv_fetch($stmt))
{
    fpassthru(sqlsrv_get_field($stmt, 0));
    echo "</br>";
}

 

  

Retrieving the hierarchyid type also requires calling the .ToString() method on the type in the query, but this time the method returns a nvarchar(4000) data type. The SQL Server 2005 Driver for PHP returns this type as a string by default, so retrieving this type with sqlsrv_fetch_array or the combination of sqlsrv_fetch/sqlsrv_get_field should be straight forward. However, again note that SQL Server does not return a column name by default when the .ToString() method is called on the column:

Retrieving hierarchyid type using sqlsrv_fetch_array:

$tsql = "SELECT EmployeeName, Title,

                OrgNode.ToString() AS org
         FROM HierarchyTable";
$stmt = sqlsrv_query($conn, $tsql);

 

while($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))
{
     print("<pre>");
     print_r($row);
     print("</pre>");
}

 

  

Retrieving hierarchyid type using sqlsrv_fetch/sqlsrv_get_field:

$tsql = "SELECT EmployeeName, Title,

                OrgNode.ToString() AS org
         FROM HierarchyTable";
$stmt = sqlsrv_query($conn, $tsql);

 

while($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))
{
     echo sqlsrv_get_field($stmt, 0)."</br>";
     echo sqlsrv_get_field($stmt, 1)."</br>";
     echo sqlsrv_get_field($stmt, 2)."</br>";
}

 

  

Inserting New Data Types

With all of the new SQL Server 2008 data types you can simply send strings to the database. You will have to pay some attention to the format of the strings you send, but the server does a fairly good job of converting strings to the correct data type.

With the date, time, datetimeoffset, and datetime2 types, the format of the strings must be close to the expected format of the destination type. In some cases, however, the server will not be able to convert the supplied string to the desired data type (resulting in an error from the server).

In this example I send strings that closely match the format of the destination type:

$tsql = "INSERT INTO DateTimeTable (myDate,
                                    myTime,
                                    myDateTimeOffset,
                                    myDatetime2)
         VALUES (?, ?, ?, ?)";

 

$params = array(

            date("Y-m-d"), // Current date in Y-m-d format.
            "15:30:41.987", // Time as a string.
            date("c"), // Current date in ISO 8601 format.
            date("Y-m-d H:i:s.u") // Current date and time.
          );


$stmt = sqlsrv_query($conn, $tsql, $params);

 

  

With the geometry and geography types, strings must conform to the Open Geospatial Consortium (OGC) Well-Known Text (WKT) format. (For more information, see Designing and Implementing Spatial Storage.) In the following example, I send POLYGON and LINESTRING data to the GeoTable:

 

$tsql = "INSERT INTO GeoTable (myGeometry,
                               myGeography)
         VALUES (?, ?)";

 

$params = array(

           'POLYGON ((0 0, 150 0, 150 150, 0 150, 0 0))',
           'LINESTRING(-122.360 47.656, -122.343 47.656)'

          );

 

$stmt = sqlsrv_query($conn, $tsql, $params);

 

  

And finally, the hierarchyid type can be sent to as a string as long as the string is a series of slashes separated by numerical values (e.g. /2/3/1/). The following code inserts an employee just below the CEO level (as a sibling to Sariya in the hierarchy defined in the HierarchyTable): 

 

$tsql = "INSERT INTO HierarchyTable
               (EmployeeName, Title, OrgNode)
         VALUES (?, ?, ?)";

 

$params = array("Brian", "Specialist", "/2/");

$stmt = sqlsrv_query($conn, $tsql, $params);

 

  

That’s it. Please let me know if this has been useful/helpful. In my next post, I’ll address how to use the new filestream capabilities of SQL Server 2008 from PHP scripts.

 

Thanks.

Brian Swan

Programming Writer, SQL Server 2005 Driver for PHP

October 2008 Cumulative Update Now Available!
 

It is my pleasure to announce that Microsoft has released an update to version 1.0 of the Microsoft SQL Server 2005 Driver for PHP.  The update to the driver is the October 2008 Cumulative Update and it contains fixes to two high priority bugs reported via the MSDN Forum:

  1. Failure when executing UPDATE / DELETE query that affects 0 rows
  2. Default packet size exceeds SSL max. limit

Thanks to W1lz0r and Jos Juffermans for bringing those issues to our attention.

 

The updated version of the driver is available for download on the MSDN download site and replaces the v1.0 RTM release.  We have also published the updates to the source on the CodePlex site.

 

Enjoy!

 

David Sceppa

Program Manager - Microsoft SQL Server Driver for PHP

How and Why to Use Parameterized Queries

I recently attended ZendCon 08 in Santa Clara, CA with David Sceppa, Program Manager for the SQL Server 2005 Driver for PHP. We hosted a "Birds of a Feather" (BOF) session and were happy to get feedback and field lots of good questions from developers who are using our driver. During the session, our discussion touched on how and why to use parameterized queries. The focus of this post is to recap that conversation and to demonstrate how to use parameterized queries with the SQL Server 2005 Driver for PHP.

A parameterized query is a query in which placeholders are used for parameters and the parameter values are supplied at execution time. The most important reason to use parameterized queries is to avoid SQL injection attacks.

Let's take a look at what can happen if we don't use parameterized queries. Consider the following code that concatenates user input with SQL syntax:

$name = $_REQUEST['name'];

$email = $_REQUEST['email'];

$sql = "INSERT INTO CustomerTable (Name, Email)

        VALUES ('$name', '$email')";

Now suppose a user enters the following data:

           FormInput

The resulting SQL query (defined by $sql) is the following:

INSERT INTO CustomerTable (Name, Email)
    VALUES ('Brian', 'bswan@microsoft.com');
DROP TABLE CustomerTable;
PRINT 'Gotcha!'--')

This is a perfectly valid SQL query, and, as you can see, the results of executing this on the server (with a function such as sqlsrv_query) would not be desired. This does assume that the user has some knowledge of your database (or that he guessed the table name correctly) and that credentials used to access the server have sufficient permissions to drop a table. However far-fetched these assumptions may seem, when you construct SQL queries by concatenating user input with SQL syntax you run the risk of the user supplying input that may cause your query to do something that you had not expected.

The simplest and most effective way to avoid the scenario described above is to use parameterized queries. Here is how the code above would look when using a parameterized query:

$name = $_REQUEST['name'];
$email = $_REQUEST['email'];
$params = array($name, $email);
$sql = 'INSERT INTO CustomerTable (Name, Email) VALUES (?, ?)';

Now, to execute the query, we just pass an open connection ($conn), the SQL query ($sql), and the parameter array ($params) to the sqlsrv_query function:

$stmt = sqlsrv_query($conn, $tsql, $params);

(The sqlsrv_query function returns a PHP statement resource.)

The difference here (as opposed to concatenating user input with SQL syntax) is that a query plan is constructed on the server before the query is executed with parameter values. In other words, a query plan is constructed on the server for this query:

INSERT INTO CustomerTable (Name, Email) VALUES (?, ?)

When you execute this query using parameterized values and the same user input , only the INSERT query is executed. The server accepts the user input of

bswan@microsoft.com'; DROP TABLE CustomerTable; PRINT 'Gotcha!'--

and inserts that entire value into the Email field. Using a parameterized query prevents the user input from leading to SQL injection. Plus, using a parameterized query allows you to handle less malicious scenarios, such as where the user supplies a value like "O'Leary" without forcing you to replace single quotes with double single quotes.

For more information about avoiding SQL injection attacks, see SQL Injection. For more information about how to execute parameterized queries, see How to: Perform Parameterized Queries, How to: Execute a Single Query, and How to: Execute a Query Multiple Times in the driver documentation.

Regardless of which database server or driver you use, a best practice for writing code is to use parameterized queries for security reasons.

Please let me know if this post has been helpful, and/or post questions or other comments.

Thanks.

Brian Swan
Programming Writer, SQL Server Driver for PHP

Greetings from ZendCon08!

Brian Swan and I are here at ZendCon08 to talk with PHP developers about the SQL Server Driver for PHP.  Come visit us in the Microsoft booth and say, "Hi!"

 

We've also signed up for an un-con session Wednesday at 11:00 to get more feedback from the community.  For more information, see the un-con wiki page:  http://www.zendcon.com/ZendCon08/public/wiki/UnCon_Talks

 

David Sceppa

Program Manager - Microsoft SQL Server Driver for PHP

New SQL Server Driver for PHP Whitepaper Available

A new technical article covering the SQL Server 2005 Driver for PHP is now available on MSDN: Accessing SQL Server Databases with PHP. This article highlights many of the features of the driver by examining portions of the Example Application that is available in the documentation. Here’s the table of contents to pique your interest: 

 

- Introduction

- Loading the Driver

- Configuring the Driver

- Creating a Connection

- Connection Pooling

- Executing a Query

- Retrieving Data from the Server

- Retrieving Data as an Array

- Retrieving Images

- Sending Data to the Server

- Sending Images to the Server

- Moving Through Groups of Result Sets

- Handling Errors and Warnings

- Resources

- Conclusion

 

Brian Swan

Programming Writer – SQL Server Driver for PHP
Unicode Support and Endianness

We’ve seen more activity on our MSDN Forum over the past couple weeks (yay!) and there have been a few threads about how/if the SQL Server Driver for PHP supports Unicode.  Rest assured, the SQL Server Driver for PHP does support Unicode.  The driver expects Unicode strings to be in little endian format, since that's what SQL Server uses.

 

When you convert strings between different encodings in PHP using iconv or mb_convert_encoding, PHP will assume big endian format if you do not specify endianness.  In other words, you must specify "ucs-2le" instead of just "ucs-2".  The example in our documentation for working with UTF-8 strings did not specify endianness so the strings were converted to and from Unicode in big endian format.  We're in the process of correcting that error and apologize for the confusion.

 

For more information on endianness and the origin of the term "endian", see Michael Kaplan's blog post or the Wikipedia entry on endianness.

 

David Sceppa

Program Manager - Microsoft SQL Server Driver for PHP

More Posts Next page »
Page view tracker