Welcome to MSDN Blogs Sign in | Join | Help

does not contain a definition for

Have you heard about extension methods yet? If you have then you probably know exactly what the title of this blog means. I can see this becoming one of the most asked about exceptions in the coming release. At least until the dust settles.

 

<Edit>

If you are running into the above exception, you can fix this without reading the entire post. Just add a "using System.Linq;" to your using block

 

if you are seeing the "cannot be inferred from the usage." exception mentioned below  and you are trying to use Linq over DataSets you need to add a reference to System.Data.Entity. You will have to hunt for it under the 3.5 framework install directory. 

</Edit>

 

So what is going on? Let’s write some code, a simple Linq over DataSets example that assumes you have the latest ctp installed.

 

using System;

using System.Data;

using System.Data.Common;

 

namespace ConsoleApplication3

{

    class Program

    {

        static DataSet GetDataSet()

        {

            DataSet ds = new DataSet();

            DataTable dt = new DataTable("Customer");

            dt.Columns.Add("Name");

            dt.Columns.Add("Country");

            dt.Rows.Add(new string[] { "Name1", "Country1" });

            dt.Rows.Add(new string[] { "Name2", "Country1" });

            dt.Rows.Add(new string[] { "Name3", "Country3" });

            ds.Tables.Add(dt);

            return ds;

        }

        static void Main(string[] args)

        {

            DataSet ds = GetDataSet();

            var query = ds.Tables["Customer"].AsEnumerable().Where(c => c.Field<string>("Country") == "Country1")

                .Select(c => c.Field<string>("Name"));

            foreach (string s in query)

            {

                Console.WriteLine(s);

            }

        }

    }

}

 

For completeness sake I have added a very crude GetDataSet method, please excuse this code bloat but I hate code samples that are not self contained. Once we get a DataSet we do a very simple Linq query to return all the Names of customers living in Country1.  

 

The code looks fairly reasonable, we hit compile and… Exception!:

 

'System.Data.DataTable' does not contain a definition for 'AsEnumerable' and no extension method 'AsEnumerable' accepting a first argument of type 'System.Data.DataTable' could be found (are you missing a using directive or an assembly reference?)    

 

This is a fairly nice exception, readable text and it asks you whether you are missing a using or assembly reference (it turns out we are missing both). If you are reading this blog it is almost certain that you know exactly what the problem is, but let’s pretend that you don’t know what is going on here and do a quick “Microsoft live” search on the exception above. A quick search will almost certainly tell you that you are missing a “using System.Linq” statement. Let’s add this to our code above:

 

using System.Linq; //add this to the code above.

 

So, are we done? Not really, this is where things get interesting. When we go to compile the code we get the following gem:

 

The type arguments for method 'System.Linq.Enumerable.AsEnumerable<TSource>(System.Collections.Generic.IEnumerable<TSource>)' cannot be inferred from the usage. Try specifying the type arguments explicitly

 

This is a particularly useless exception. Not only does it make almost no sense, the suggestions is horrible (do not try to specify the type arguments explicitly). What went wrong?

 

The problem is that for the nice linq over DataSet code above to work we need to use the System.Data.Entity.dll EnumerableDataTable. To do this we need to add a reference to System.Data.Entity.dll in our project. Unfortunatelly there is no good way for System.Linq to throw a better exception here, it just can't know that you are trying to use the wrong AsEnumerable.

 

//Add System.Data.Entity.dll to the list of References of your project.

 

Finally your project will compile and run as expected, congratulations!

 

 

 

Ok, let’s go for some extra credit. Now that we have determined that we really needed to add the System.Data.Entity.dll reference why don’t we just remove the “using System.Linq” statement?

 

Very simple, because if we remove System.Linq we get the following exception:

 

'System.Collections.Generic.IEnumerable<System.Data.DataRow>' does not contain a definition for 'Where' and no extension method 'Where' accepting a first argument of type 'System.Collections.Generic.IEnumerable<System.Data.DataRow>' could be found (are you missing a using directive or an assembly reference?)

 

The reason for this is left as an exercise to the reader (I have always wanted to say that *grin*)

 

Rambling out

Posted by angelsb | 2 Comments
Filed under:

Blogging about link Extensions

There are not a lot of guidelines about blogging, the one that I like to adhere to is that I only blog about bits that you can go and play with. This ends up creating gaps in my blogging history as I move to new unpublished products. I have moved, oh not far I am still in the same team just switched products. The Jdbc driver is doing great so I started looking for new challenges. In this case the new challenges may well have gotten the better of me... we shall see. In any case I am now working on the Linq extensions for ado.net, this will be Linq over Dataset and Linq over Entities for now, I am sure more will come. I am really biting at the bit here, this is really cool tech. Here is a blog on Linq extensions over DataSet to get things started:

http://blogs.msdn.com/adonet/archive/2007/01/26/querying-datasets-introduction-to-linq-to-dataset.aspx

Posted by angelsb | 2 Comments

JDBC V1.1 Distributed Transactions with Read Committed and Snapshot isolation.

One of the biggest changes you may not have noticed in the v1.1 2005 JDBC driver is that we have changed the default isolation level for Distributed Transactions from Serializable to Read Committed. This is in line with other JDBC drivers like our 2000 jdbc driver.

 

In this blog I am going to discuss the two questions that I had when I first heard about this change.

 

Q1>How do I set the isolation level of distributed transactions back to Serializable for those applications that require it (boring!)

Answer> To change the isolation level you just need to set the following connection property:

conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

Make sure you set this _before_  calling XAResource start!

 

Of course you can set any isolation level you want here, transaction_none will just give you the default isolation level . To specify snapshot isolation level (_after_ you have set up the database to use snapshot with ALTER DATABASE <database name> SET ALLOW_SNAPSHOT_ISOLATION ON) you can do the following:

import com.microsoft.sqlserver.jdbc.*;

/…/

conn.setTransactionIsolation(SQLServerConnection.TRANSACTION_SNAPSHOT);

 

If you don’t want to import the sqlserver.jdbc namespace you can use the following:

            conn.setTransactionIsolation(4096);

 

 

Q2>how do I set the default isolation level of Distributed Transactions to Snapshot (woo!)

Answer> This is what you are most likely going to want if you are looking to minimize lock contention.

The first thing you need to do is to set the _default_ isolation level of the database to snapshot like this:

ALTER DATABASE <databasename> SET READ_COMMITTED_SNAPSHOT ON

 

Once this is done all Read Committed (the default isolation level for v1.1 2005 JDBC driver!) transactions will behave as snapshot. You are done.

 

 

Here is some code for you to play with, I realize that it may look a little intimidating since we have to roll our own XIDs, but you can pretty much just ignore anything outside of the DoDTC method.

 

import java.net.Inet4Address;

import java.sql.*;

import com.microsoft.sqlserver.jdbc.*;

 

import javax.sql.XAConnection;

import javax.transaction.xa.XAResource;

import javax.transaction.xa.Xid;

import com.microsoft.sqlserver.jdbc.SQLServerXADataSource;

import java.util.*;

 

 

public class transaction2 {

      public static void main(String[] args) {

            try{

                  transaction2 t = new transaction2();

                  t.DoDTC(1);

                  System.out.println("done");

            }catch(Exception e){

                  e.printStackTrace();

            }

      }

 

      private void DoDTC(int id) throws Exception

      {

            SQLServerXADataSource ds = new SQLServerXADataSource();

            ds.setURL("jdbc:sqlserver://servername;user=username;password=password");

            XAConnection xaConn = ds.getXAConnection();

            Connection conn = xaConn.getConnection();

            System.out.println("connected");

            //This is a good place to change the default isolation level of the transaction. Ex:

            //conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

           

            XAResource xaRes = null;

            Xid xid = null;

            try{

                  xaRes = xaConn.getXAResource();

                  xid = getUniqueXID();

                  System.out.println("[a] xid=" + xid.toString());

 

                  xaRes.start(xid,XAResource.TMNOFLAGS);

 

                  ResultSet rs = conn.createStatement().executeQuery("DBCC USEROPTIONS");

                  while (rs.next()) {

                        if (rs.getString(1).equals("isolation level")) {

                              System.out.println("isolation level " + rs.getString(2));

                        }

                  }

                  xaRes.end(xid,XAResource.TMSUCCESS);

                  xaRes.commit(xid,true);

                  //xaRes.rollback(xid);

            }catch(Exception ex){

                  //Important. On certain exceptions we may leak transactions!

                  //Make sure to reclaim them.

                  xaRes.forget(xid);

                  throw ex;

            }

      }

 

 

//    Returns a globally unique transaction id

      byte [] localIP = null;

      private int txnUniqueID = 0;

      private int tid=1;

      private Xid getUniqueXID()

      {

            Random rnd = new Random(System.currentTimeMillis());

            txnUniqueID++;

            int txnUID = txnUniqueID;

            int tidID = tid; 

            int randID = rnd.nextInt();

            byte[] gtrid = new byte[64];

            byte[] bqual = new byte[64];

            if ( null == localIP)

            {

                  try

                  {

                        localIP = Inet4Address.getLocalHost().getAddress();

                  }

                  catch ( Exception ex )

                  {

                        localIP =  new byte[] { 0x01,0x02,0x03,0x04 };

                  }

            }

            System.arraycopy(localIP,0,gtrid,0,4);

            System.arraycopy(localIP,0,bqual,0,4);

 

            // Bytes 4 -> 7 - unique transaction id (unique to our class instance).

            // Bytes 8 ->11 - thread id (unique to our thread).

            // Bytes 12->15 - random number generated using seed from current time in milliseconds.

            for (int i=0; i<=3; i++)

            {

                  gtrid[i+4] = (byte)(txnUID%0x100);

                  bqual[i+4] = (byte)(txnUID%0x100);

                  txnUID >>= 8;

                  gtrid[i+8] = (byte)(tidID%0x100);

                  bqual[i+8] = (byte)(tidID%0x100);

                  tidID >>= 8;

                  gtrid[i+12] = (byte)(randID%0x100);

                  bqual[i+12] = (byte)(randID%0x100);      

                  randID >>= 8;

            }

            return new XidImpl(0x1234, gtrid, bqual);

      }

}

class XidImpl implements Xid

{

      public int formatId;

      public byte[] gtrid;

      public byte[] bqual;

 

      public byte[] getGlobalTransactionId()

      {

            return gtrid; 

      }

      public byte[] getBranchQualifier()    

      {

            return bqual; 

      }

      public int    getFormatId()            {return formatId; }

 

      XidImpl(int formatId, byte[] gtrid, byte[] bqual)

      {

            this.formatId = formatId;

            this.gtrid = gtrid;

            this.bqual = bqual;

      }

      public String toString()

      {

            int hexVal;

            StringBuffer sb = new StringBuffer(512);

            sb.append("formatId=" + formatId);

            sb.append(" gtrid(" + gtrid.length + ")={0x");

            for (int i=0; i<gtrid.length; i++)

            {

                  hexVal = gtrid[i]&0xFF;

                  if ( hexVal < 0x10 )

                        sb.append("0" + Integer.toHexString(gtrid[i]&0xFF));

                  else

                        sb.append(Integer.toHexString(gtrid[i]&0xFF));

            }

            sb.append("} bqual(" + bqual.length + ")={0x");

            for (int i=0; i<bqual.length; i++)

            {

                  hexVal = bqual[i]&0xFF;

                  if ( hexVal < 0x10 )

                        sb.append("0" + Integer.toHexString(bqual[i]&0xFF));

                  else

                        sb.append(Integer.toHexString(bqual[i]&0xFF));

            }

            sb.append("}");

            return sb.toString();

      }

}

 

Posted by angelsb | 4 Comments

JDBC Snapshot isolation level

I have gotten some questions about using Sql Server 2005 with the 2005 JDBC driver. There are unfortunately a lot of misconceptions in this area. Here are some notes to make this a little bit easier to understand.

 

The first thing you need to understand about Snapshot isolation level is that it only works in a Sql Server 2005 database that has been setup for Snapshot isolation level. The second thing that you need to understand is that you can only see a snapshot of the data locked by other transactions when running from inside a transaction with snapshot isolation level. This is different from Oracle where you can always get a snapshot of the data currently locked.

 

The basics:

Setting up a Sql Server 2005 database for Snapshot goes something like this:

ALTER DATABASE <database name> SET ALLOW_SNAPSHOT_ISOLATION ON

 

Another option is to modify the default isolation level to behave like Snapshot isolation. The ALTER DATABASE statement has been upgraded to allow you to get snapshots benefits for free:

ALTER DATABASE <database name> SET READ_COMMITTED_SNAPSHOT ON

 

Once you have set up the database you can test it with the following code, note the following line:

conn2.setTransactionIsolation(4096); //4096 corresponds to SQLServerConnection.TRANSACTION_SNAPSHOT

This is where we set the JDBC isolation level to Snapshot, remove this line and the code below will deadlock!

 

create table foo (myid int primary key)

insert into foo values (1)

insert into foo values (2)

insert into foo values (3)

insert into foo values (4)

 

import java.sql.*;

 

public class test

{

      public static void main(String args[])

      throws Exception

      {

            Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");

            Connection conn = DriverManager.getConnection("jdbc:sqlserver://yourserver;integratedsecurity=true;");

            DatabaseMetaData m = conn.getMetaData();

            System.out.println("getDriverVersion  "+m.getDriverVersion());

           

            //<Lock the table foo>

            conn.setAutoCommit(false);

            Statement stmt = conn.createStatement();

            stmt.executeQuery("select * from foo");

            stmt.execute("update foo  set myid=100 where myid =1");

            //</Lock the table foo>

           

            //Now use snapshot isolation level to see the data.

            //Note: if you change the isolation level here to anything else the application will lock

            //unless you have set up the database to treat Read Committed as Snapshot.

            Connection conn2 = DriverManager.getConnection("jdbc:sqlserver:// yourserver;integratedsecurity=true;");

            conn2.setTransactionIsolation(4096); //4096 corresponds to SQLServerConnection.TRANSACTION_SNAPSHOT

            conn2.setAutoCommit(false);

            Statement stmt2 = conn2.createStatement();

            ResultSet rs2 = stmt2.executeQuery("select * from foo");

            while (rs2.next()){System.out.println("foo value= "+rs2.getString(1));}

           

            conn.rollback();

            conn.close();

            conn2.close();

            System.out.println("Done");

      }

}

Posted by angelsb | 0 Comments

JDBC 2005 v1.1 is now available.

The v1.1 2005 JDBC driver is now available for download here:

http://msdn.microsoft.com/data/ref/jdbc/

 

This is a release I am personally really proud of, and we could not have done it without you!

 

Maybe it’s the fact that not that many people in the company are doing java development, that we are using eclipse as our main IDE, junit for our testing, using JBOSS and other application servers, the Solaris machine and multiple unix flavors we play with…  Maybe it’s the short fast and furious product cycles with betas thrown in for good measure, we have shipped two products and three betas in little over a year.   

 

I like to think that one of the most important things we have going for us here is the feedback we are getting from the java community. I am not going to go and claim that we have been embraced , but there have been a set of people that just _get_ what we are trying to do here and are willing to work with us in this space. To those of you, thank you and looking forward to more of your feedback and support.

 

New features for v1.1

Integrated security:

Only supported on Windows OS, this feature allows your JDBC driver to connect to Sql Server using your Windows credentials.

 

Client Failover, Database Mirroring support:

The JDBC v1.1 driver will work with the Sql Server 2005 Database Mirroring feature: http://www.microsoft.com/technet/prodtechnol/sql/2005/dbmirror.mspx

 

When connecting to a Sql Server 2005 Database that has a failover partner the JDBC v1.1 driver will cache failover information and retry to the failover partner on primary failure. Additionally you can use the new “failoverPartner” connection string keyword to handle initial connection failure scenarios.

 

packetSize

[512..32767] Default  4096. The network packet size used to communicate with SQL Server, specified in bytes. This feature allows you to customize the network packet size to handle larger than 256MB data and to optimize performance in some scenarios.

 

 

Here are some of the important issues that got resolved this release.

For the complete list take a look at the release.txt file that ships with the v1.1 package.

NOTE: the numbers are just IDs to be used to identify specific issues, they do not have any real meaning.

 

426389 Calling Statement.execute no longer causes an SQLException with the

       message "sp_cursoropen/sp_cursorprepare: The statement parameter can

       only be a single select or a single stored procedure" for UPDATE

       WHERE queries when the selectMethod=cursor connection property is set.

 

436718 Calling PreparedStatement.execute no longer causes an SQLException

       with the message "sp_cursoropen/sp_cursorprepare: The statement

       parameter can only be a batch or a stored procedure with a single

       select, without FOR BROWSE, COMPUTE BY, or variable assignments" for

       INSERT INTO queries when the selectMethod=cursor connection property is set.

 

450639 The driver no longer throws an SQLException with the message "Server

       failed to resume the transaction, desc:  4600000001." when configuring

       SQL Server 2005 as the JBoss JMS message store.

 

435796 Executing SQL WRITETEXT statements no longer throws an SQLException

       with the message "No TDS_RET_STATUS was found for the stored procedure

       output parameters."

 

424295 DatabaseMetaData.getMaxConnections no longer throws an SQLException with

       the message "The configuration option 'user connections' does not exist,

       or it may be an advanced option" for SQL Server 2005 servers.

 

433394 Calling Statement.executeBatch after setting a BLOB value no longer throws

       an SQLException with the message "The conversion from BLOB to BINARY is unsupported."

 

435448 The driver no longer throws an SQLException with the message

       "com.microsoft.sqlserver.jdbc.SQLServerException: Incorrect syntax near '-'"

       when sending a BigDecimal prepared statement parameter value to the server

       with the 5.0 JRE.

 

Posted by angelsb | 2 Comments
Filed under:

JDBC June 2006 Community Tech Preview available.

Announcing the June 2006 Community Tech Preview of the JDBC 2005 driver: 
 
This preview contains all the changes of CTP 1 including Integrated Security, it includes rollup fixes for all the QFEs we have shipped to date and we have addressed most customer issues filed through the product feedback center.
 
To round it off we have also added a brand new feature: Client Failover. As far as I know this is the first JDBC driver to take advantage of Sql Server 2005 Database Mirroring. This feature has been fully documented for this CTP, we could really use feedback on both the functionality and the docs.
 
For a complete change list take a look at the release.txt file that ships with the package, for the first time we are providing comprehensive information on the work we have done since v1.0, let us know if this is helpful information.
Posted by angelsb | 0 Comments
Filed under:

JDBC v1.1 CTP and the Product Feedback Center

When you get down to it Customer feedback is what makes a project like this interesting and worthwhile. This is the first project I have worked in where the Product Feedback Center has made such an important difference and a BIG thank you to those of you that have taken the time to file bugs directly on the JDBC provider: http://lab.msdn.microsoft.com/productfeedback/default.aspx

 

I want to make sure that you understand that we take these bugs very seriously. We triage them daily and treat them as the highest priority bugs come release time. Here is a list of customer bugs that we have received since we shipped RTM.

 

All of these bugs have been fixed in our latest JDBC V1.1 CTP. you can download this CTP by contacting the JDBC project manager directly at shelbyg<at>Microsoft<dot>com.

 

 

Bug Details: Statement.execute() returns incorrect results for stored procedures

http://lab.msdn.microsoft.com/ProductFeedback/viewfeedback.aspx?feedbackid=dc43eb80-69b5-4137-8671-9f4e813ce259

 

Bug Details: JDBC 2005 GA Driver error when running a callablestatement on SQLServer 2000

http://lab.msdn.microsoft.com/ProductFeedback/viewFeedback.aspx?feedbackid=3fcf439d-79ed-44d9-bbbf-320e4ee25d89

Exception Message: “SQL Error: A server cursor is not allowed on a remote stored procedure or stored procedure with more than one SELECT statement. Use a default result set or client cursor”

 

Bug Details: JDBC Driver executeBatch does not work with INSERT statement

http://lab.msdn.microsoft.com/ProductFeedback/viewFeedback.aspx?feedbackid=e13a8009-7466-4803-ba19-6bfd4b5f8966

Exception Message: “java.sql.BatchUpdateException: com.microsoft.sqlserver.jdbc.SQLServerException: sp_cursoropen/sp_cursorprepare: The statement parameter can only be a single select or a single stored procedure.“

 

Bug Details: SQL2005 JDBC Driver bug with time zone handling **Available as a QFE**

http://lab.msdn.microsoft.com/ProductFeedback/viewFeedback.aspx?feedbackid=e73c8b79-53a6-4779-98f1-71c0043179eb

 

Additional customer issues addressed that have no associated external bugs:

 

Bug Detail: Using sendStringParametersAsUnicode does not work with XA transactions.

Exception Message: “java.sql.SQLException: Failed to create the XA control connection.”

 

 

 

Posted by angelsb | 0 Comments
Filed under:

JDBC v1.1 CTP Using Type 2 Integrated Security

To use the Type 2 Integrated Security feature with the v1.1 CTP (if you don't have this already make sure to get it! see my previous blog) you only need to make sure that the Windows authentication DLL is visible to the application that is going to use it (This usually means it is in the same directory or in a directory specified in your Path). Then specify integratedSecurity in your connection string and you are done.

The Windows authentication DLL is located under:

\sqljdbc_1.1\enu\auth\x86\sqljdbc_auth.dll  //for x86 client OS versions
\sqljdbc_1.1\enu\auth\x64\sqljdbc_auth.dll  //for AMD 64 clients running Windows 2003 64bit OS  

import java.sql.*;

public class test {
 public static void main(String[] args) {
  try {
   java.lang.Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
   Connection conn = java.sql.DriverManager.getConnection("jdbc:sqlserver://servername;integratedSecurity=true");
   System.out.println("Connected!");
   conn.close();

  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }
}

 

Posted by angelsb | 0 Comments

JDBC V1.1 CTP Available

I am proud to say that the first community preview release of the SQL Server 2005 JDBC v1.1 driver is now available.

This build includes Type-2 integrated authentication  for Windows XP and Windows 2003 clients (windows 2000 client support to come in the next CTP). It also incorporates bug fixes from customer feedback of the v1.0 driver and a fair ammount of plumbing under the covers. We really want to get as much feedback on this as possible.

To get the v1.1 CTP1 please email the JDBC Project Manager directly at
shelbyg<at>microsoft<dot>com.

I have a bet going with him on the number of requests he will receive so don't let me down!

Posted by angelsb | 0 Comments
Filed under:

JDBC 2005 beta 2 getting started with SQLEXPRESS

Sql Express is going to be a good match for the Sql Server 2005 JDBC provider, it is free, it is relatively easy to set up and deploy and it has all the power of Sql Server 2005 for low end applications.

There are a few  things to keep in mind when using Sql Express with the JDBC driver:

·         You need to enable mixed mode security when you install the server so that you can connect using user name and password.
·         Make sure that the sqlbrowser is enabled.  Sql Server configuration Manager ->SQL Server 2005 Services ->SQL Server Browser ->Start
·         The JDBC provider only works with the TCP/IP protocol which is disabled by default on Sql Express. You need to enable the TCP/IP Protocol from the SQL Server Configuration Manager that ships with SqlExpress and re-start the server. Look under SQL Server 2005 Network Configuration -> Protocols for SQLEXPRESS-> TCP/IP->Enable.

 

Once you have set up Sql Express accordingly you can connect like this:
 
import java.sql.*;
 
public class test{
  public static void main(String[] args) {
    try{
       java.lang.Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
       Connection c = java.sql.DriverManager.getConnection("jdbc:sqlserver://localhost\\SQLEXPRESS;user=myuser;password=mypass;");
       System.out.println("Connected!");
    }catch(Exception ex){
       ex.printStackTrace();
    }
  }
}
 
If you are interested enough in this driver to have read this far you may be interested in learning more about our Data Coercion story:
http://blogs.msdn.com/dataaccess/archive/2005/10/19/482792.aspx 
 
Rambling out, the information in this blog is provided "AS IS" and confers no rights.

 

Posted by angelsb | 4 Comments

JDBC Beta 2 connection string properties

As you have probably seen by now we have shipped beta 2 of the Sql Server 2005 JDBC driver.  

http://www.microsoft.com/sql/downloads/2005/jdbc.mspx

 

We have put a lot of work into this beta and it is with some trepidation that I see it go out the door. We have made a lot of hard decisions, our design philosophy has been to "do it right". This means that there are a lot of changes, some that may break some scenario that worked before (those are the ones I am most interested in hearing about). I can assure you that for every single change we have spent hours pouring over the "Maydene Fisher/Jon Ellis/Jonathan Burce, JDBC API Tutorial and Reference, third edtion" trying to do the right thing and I want to write a few blogs about some of these changes.

 

I will start with the easy one, we have cleaned up the connection string properties. We no longer allow duplicate connection string values, we now allow escaping values (using the '{' and '}' brackets) we will now throw an exception whenever you enter a name value pair that we don't understand etc. Besides all of that house keeping we have made some hard decisions, feel free to leave feedback on any of the following:

 

codepage has been removed, we now keep track of the codepage that you are using. There are some problems with this approach, we are currently supporting the 850+ codepages that the JVM supports. The problem is that there are a number of Codepages that are not supported by the VM that will now fail! In beta 2. We feel that explicitly failing is better than the possible data corruption that we were exposing before. Important: If you depend on a specific codepage that is _not_ supported by the JVM please contact us directly.

 

selectMethod has become forwardReadOnlyMethod. We wanted to make it very explicit that this method only affects the statements opened with Forward Only Read Only cursors (the default)

 

workstationID has been added, use this to identify the specific workstation in various SQL Server profiling and logging tools

 

trustedAuthentication has become integratedSecurity and ntlmAuthentication + domain have been removed. We are working on this feature but quite frankly we are seriously looking at this feature.

 

sqlVersion has been removed, we only support Sql Server 2000 and Sql Server 2005.

 

enableFailover has been removed, the implementation had some security issues so we are removing it until we can do it correctly.

 

logfile has been removed we have moved diagnostics to java.util.logging.Logger

 

asciiStringParameters becomes logically inverse sendStringParametersAsUnicode. We don’t always send the string parameters as ascii when not sending them as Unicode, renaming this property makes this clearer.

 

booleanLiterals has been removed, we were parsing SQL statements and translating Oracle boolean literas to Sql Server numeric values. This is a very incomplete feature so we have decided to postpone Oracle migration features to a later release.

Posted by angelsb | 4 Comments
Filed under:

JDBC getting started with the 2005 JDBC driver for Sql Server 2000 and Sql server 2005

If you had told me a year ago that I would be working on a JDBC driver I would have honestly thought you were kidding. If you had suggested a ship date concurrent with Sql Server 2005 (within a reasonable window) I would have laughed out loud. This is turning out to be a tremendously interesting project in so many ways. I find that I am having a very hard time blogging about this project in general terms, so I am just going to start at the beginning.

Getting started with the new 2005 JDBC driver,

download the beta driver from:
http://www.microsoft.com/sql/downloads/2005/jdbc.mspx

sample code:
import java.sql.*;
public class test{
  public static void main(String[] args) {
    try{
         java.lang.Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
         Connection connection1 = java.sql.DriverManager.getConnection("jdbc:sqlserver://server;user=myuser;password=mypassword");
         System.out.println("Connected!");
        
         Statement statement1 = connection1.createStatement();
         ResultSet resultset1 = statement1.executeQuery("select * from foo");
            while(resultset1.next() ){
                        System.out.println("Foo result = "+resultset1.getObject(1) );
            }
    }catch(Exception ex){
        ex.printStackTrace();
    }
  }
}
 
JDBC 3.0 is a well stablished standard, there are no surprises in the code itself. More interesting however is what is going on under the covers, and more importantly what _should be going on_. This is where we are looking for feedback, let us know what you think:
 
java.lang.Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
You may have a sense of deja vue looking at the name of the new driver, yes you are not mistaken we just switched the name of the old driver around. Confusing? Well maybe a little bit, but have you tried running names through legal? Let me just make it very clear that this driver is not in any way related to the 2000 driver.
 
Connection connection1 = java.sql.DriverManager.getConnection("jdbc:sqlserver:/server;user=myuser;password=mypassword");
This is actually a lot more interesting than it looks (especially considering that this is a non pooled connection). I guess a good starting point would be asking the following question: What is a connection string url? Well, it is url-looking thanks to that “:/” but what it is really for is to allow you to provide information to connect to your server. Lets go over some of the confusing concepts here:
 
Duplicate Keywords:
What happens when you enter more than one "user=" keyword value pairs? In ado.net and in oledb the last keyword wins, in odbc the first keyword can win (driver dependent). This has been historically done so that you can override an existing connection string without having to parse it, but it is confusing and bug prone. I would like to break with tradition and throw an exception on duplicates, what do you think?
 
Synonyms/Aliases:
Another source of confusion, is it database? databaseName? Both? In what order? Synonyms may be necessary for backwards compatibility but this is a 1.0 driver. The current beta has an interesting bug with synonyms and I see no reason to keep them, any thoughts? 
 
Handling unknown keywords:
Generic drivers like Oledb or Odbc drivers need to be able to ignore any connection string keywords that they don’t understand. This is not true for a type four driver, I don’t see a good reason to allow these and many reasons to avoid them, we probably need to throw an exception here.
 
Case sensitivity:
Is database the same as Database? Well it turns out that String manipulation in java is not particularly performant, it is well worth enforcing case sensitivity on connection string keywords to avoid the performance penalty. As long as we start throwing on unknown keywords (see above) this will not be a major customer pain point.
 
Separators:
Currently we allow “;”, “/”, “:” and a few more separators between connection string keywords, I am not sure that this is a clean model. We should be able to stick to one separator (“;”) plus the (“:”) for port numbers, what do you think?
 
Escape characters:
This is a tough one, there is no doubt that we need them, what do we do with names with separators (see above) embedded in them? What about empty as in empty database or empty password? The simplest way to handle this would be to use something like “ ‘ “ as a separator and force you to escape any single quotes that you are going to use in your connection string, ex: user=’O’’neil’. This is not an ideal solution.
 
 
Statement statement1 = connection1.createStatement();
I will definitely have to have a separate blog about this, there is just too much going on under the covers for a quick overview. I am just going to point out that currently by default we are using a pseudo firehose cursor that may not scale well. I am personally not a great fan of server side cursors but a) JDBC requires them and b) the JDBC api seems to be geared towards using cursors. We may have to make some changes in this space.
 
 
ResultSet resultset1 = statement1.executeQuery("select * from foo");
Finally a straightforward code statement, you executeQuery and you get a resultset.
 
Rambling out, the information in this blog is provided "AS IS" and confers no rights.

 

Posted by angelsb | 4 Comments

JDBC the new member of the DataWorks family, Sql Server 2005 JDBC Driver (beta 1)

One of the hardest things for me this last few months was to go into “radio silence” over the project I am currently working on. Now that we have officially shipped a beta I can come back to blogging, albeit from a slightly different angle.


Microsoft has determined that there is a business need for a type four JDBC driver to Sql Server 2000 and 2005, this driver will have full support including PSS and a full time product group to not only maintain this product but to enable new functionality going forward. Make no mistake about it we are truly serious about supporting and improving this driver.

Presenting the Sql Server 2005 JDBC Driver (beta 1)
http://www.microsoft.com/sql/downloads/2005/jdbc.mspx

What it is:
Type 4 JDBC driver to Sql Server 2000 and Sql Server 2005. Designed to work on Java 1.4.2

Expected release date
Of course we cannot guarantee a release date, but this driver is intended to ship within a reasonable window of Sql Server 2005 launch.

Primary discussion newsgroup (Hope to see you there soon):
microsoft.public.sqlserver.jdbcdriver

Entering a bug:
Go to http://lab.msdn.microsoft.com/productfeedback/default.aspx

      Product/Technology:
          SQL Server 2005
 
      Version:
          SQL Server 2005 Community Technology Preview June 2005 - Developer Edition
 
      Product Language:
          English
 
      Category:
          JDBC Driver
 
For me walking into the JDBC space is a great challenge, there is a lot that I don’t know and given the extreme time constraints placed on this product I have very little time to get up to speed on this. For me the fastest way to do this has always been through customer feedback.
If you or your company is interested in trying this JDBC driver drop me a comment. Bugs, suggestions, concerns and “must have” feature requests always welcome.

 

Posted by angelsb | 4 Comments

Say hello to the DataWorks blog

It is a fair assumption that you have not heard about DataWorks, that is ok most folks arround Microsoft haven't either. MDAC on the other hand probably sounds familiar... DataWorks is the Microsoft Product Unit in which I work, it is responsable for most of the general data access technologies in Microsoft. If you are reading this blog you may also be familiar with many of DataWorks other "inmates": Brad Rhodes, Pablo Castro, Sushil Chordia, David Sceppa, Shelby Goerlitz ...

Now you can contribute to this product unit directly :

http://blogs.msdn.com/dataaccess  

So far it consists of only a welcome message from our PUM (Product Unit Manager) - Alyssa.  Expect good things in the future!

Posted by angelsb | 0 Comments

ADO.NET 2.0 and System.Transactions, downlevel functions and nested scopes

As with most of my blog posts this started as a customer question, the core of the problem was that he was not sure how he was expected to use TransactionScopes in methods that call other methods or "downlevel" functions as he named them.

There are two important concepts to keep in mind.

1) downlevel functions should not know or care about what happens on the functions that call them. Don't  pass TransactionScope as properties, don't assume that there is an existing transaction, etc. 

2)You can nest TransactionScopes (with the default TransactionScopeOption, "Required") or isolate downlevel functions from outer TransactionScopes (with the optional Suppress and RequiresNew TransactionScopeOption(s) )

Here is some pseudo code for a small stress app I wrote that shows some of this off: 

InsertMoneyIntoAccount() //doesn’t use transactions

RemoveMoneyFromAccount() //doesn’t  use transactions

TransferMoney() //creates TransactionScope then calls RemoveMoney and InsertMoney

TransferMoneyWithExtraCharges //creates TransactionScope, calls TransferMoney and RemoveMoneyFromAccount

// the TransactionScope in TransferMoney will be nested inside the outer Scope.

A more advanced (and to my way of thinking less recommended) concept is nesting Scopes where you do not want the behavior in the downlevel functions to affect outer scopes, you can do this by using the TransactionScopeOption.RequiresNew and Suppress enum values.

TransferVerifyAndTransfer() //creates a TransactionScope, calls Transfer, uses retry logic to call VerifyTransfer multiple times and once the transfer is verified calls Transfer again. The main problem here is that we don’t want a fatal exception in VerifyTransfer to roll back the transaction since we are building in logic to retry on exception.

VerifyTransfer() //creates a TransactionScope with TransactionScopeOption.Suppress, this guarantees that it will not affect the scopes in uplevel functions.

//It is important to realize that some exceptions even when handled can affect the transaction. In the example below if we don’t use the Suppress option for B’s TransactionScope then the ExecuteNonQuery (level 20 exception) will cause A’s outer TransactionScope to be rolled back.

 

            public void A(){  

                using (TransactionScope transactionscope1 = new TransactionScope()){

                        this.B( );

                        transactionscope1.Complete()

                }

            }

            public void B(){  

             //always create a TransactionScope with the “using” language construct.

             using (TransactionScope transactionscope1 = new TransactionScope(TransactionScopeOption.Suppress)){

                        //always create a connection with the “using” language construct.

                        using (SqlConnection sqlconnection1 = new SqlConnection(connectionstring)){

                                    sqlconnection1.Open();

                                    SqlCommand sqlcommand1 = sqlconnection1.CreateCommand();

                                    sqlcommand1.CommandText = "raiserror ('my error', 20, 20) with log";

                                    try {

                                                sqlcommand1.ExecuteNonQuery();

                                    }

                                   catch(Exception e){

                                                Console.WriteLine(e.Message);

                                    }

                                    }

                                    transactionscope1.Complete();

                        } //Here sqlconnection1 is _guaranteed_ to be disposed. I think it is a good idea to do this before the scope is disposed.

                 }

            }

Rambling out: This post is provided "AS IS" and confers no rights.

Posted by angelsb | 5 Comments
More Posts Next page »
 
Page view tracker