ODBC Escape Sequences
ODBC Escape Sequences

Within the field of computer science, competing products often implement standards differently. Database management systems (DBMSs), with their different syntaxes for SQL queries, are no exception. How can we write queries within our applications that will be compatible with several DBMSs, especially when we need to use functionality that different DBMSs have implemented differently?

Recently, Technomedia Training, Inc., was awarded a contract to deliver our core product, a Web-based human resources management software application, to a new client. The client would be hosting the application, and the IT department's infrastructure and expertise were strictly Oracle-based. However, our application was designed to work with SQL Server 2000. Our challenge was to transform our Web application to make it compatible with an Oracle8i database.

The first step was to convert the database and all its objects from SQL Server 2000 to Oracle8i. (We employed some interesting conversion techniques, but they're a whole subject themselves and we won't cover them in this article.) Next, we discovered that many of the queries within the application didn't work anymore with the Oracle8i database because of differences in implementation of the SQL standard. For example, the following outer join query in Transact-SQL works with SQL Server 2000 but is incompatible with Oracle8i:

SELECT EMP.ID, EMP.NAME, DEP.NAME
FROM EMPLOYEES EMP
LEFT OUTER JOIN DEPARTMENTS DEP ON
DEP.DEPARTMENT_ID = EMP.ID
In Oracle8i, the syntax is:

SELECT EMP.ID,EMP.NAME.DEP.NAME
FROM EMPLOYEES EMP, DEPARTMENTS DEP
WHERE EMP.DEPARTMENT_ID = DEP.ID(+)
We looked at several alternatives for resolving this problem, and concluded that ODBC escape sequences are the best way to make our application's queries compatible with several DBMSs.

Technological Architecture
Our core product is a Web application that uses a standard, three-tier architecture. A ColdFusion Server works with an IIS Web server to run the application code. The ColdFusion Server uses an ODBC driver to connect to the database. Users' Web browsers call up pages from the ColdFusion/IIS servers using the HTTP protocol, and the application works with a SQL Server 2000 database - we need to make it compatible with Oracle8i (see Figure1).

Dealing with DBMS-Specific Queries
To deal with syntax differences among DBMSs, we considered three possible solutions. We could:

1.   Change the application's queries to remove the DBMS-specific elements.
2.   Put conditional statements around all queries with DBMS-specific terms and have both syntaxes present in the code. Then, depending on which DBMS the application is connected to, the appropriate syntax would be used.
3.   Use ODBC escape sequences and let the ODBC driver map to the DBMS-specific syntax.

We'll examine these three possibilities next.

Simple SQL
The first option for dealing with DBMS-specific queries is to remove all terms that are DBMS-specific and to use only SQL syntax that is common to all DBMSs. For example, the SQL Server 2000 syntax for concatenating a first and a last name looks like this:

<cfquery name = "qsEmployees" datasource="Sigal">
SELECT first_name + ' ' + last_name AS name
FROM employees
</cfquery>
Instead, the concatenation could be done outside of the query like this:

<cfquery name = "qsEmployees" datasource="Sigal">
SELECT first_name, last_name, NULL AS name
FROM employees
</cfquery>
<cfloop query = "qsEmployees">
<cfset tmp = QuerySetCell (qsEmployees, "name",
qsEmployees.first_name & " " &

qsEmployees.last_name,
qsEmployees.CurrentRow)
</cfloop>
The advantage of this solution is that the query will work with all types of DBMSs and with all types of drivers, including non-ODBC drivers. However, we discarded this solution because of its disadvantages. If we limit our queries to a lowest-common-denominator SQL, we can no longer use some functionality that is essential to our application. There's no way to execute outer joins, and most date and time functions are excluded. All queries have to be relatively simple, which implies having to restructure certain queries or even sub-dividing them to make them more compatible. In addition, a lot of the processing is transferred from the database to the application code. This incurs a performance penalty, since the database server is optimized for many of the data operations, while the application server is not.

Different Versions of Incompatible Queries
The second option for making DBMS-specific queries compatible with several DBMSs is to create different versions of the query, each version containing DBMS-specific syntax. Using this method, our concatenation example would look like this:

<cfquery name = "qsEmployees" datasource="Sigal">
<cfif application. DBType eq "ORA">
SELECT first_name || ' ' || last_name AS name
FROM employees
<cfelse>
SELECT first_name + ' ' + last_name AS name
FROM employees
</cfif>
</cfquery>
The advantage of this solution is that it's relatively easy to implement. A new, DBMS-specific version of each incompatible query is written and the application executes the appropriate version for the database it's connected to. However, doubling queries makes the application code bigger. Modifying and maintaining the code becomes more complex because changes to a query need to be repeated for each DBMS-specific version. Moreover, this solution doesn't scale with other DBMSs. If one day the need arose to make the application compatible with a DBMS other than Oracle or SQL Server, the whole process would need to be repeated. The code would become even more complex and difficult to modify and to maintain.

ODBC Escape Sequences
Using ODBC escape sequences for the DBMS-specific queries is the third option for making queries compatible with different DBMSs. ODBC escape sequences are a way of coding database functionalities that have different syntaxes in different DBMSs. As we have seen, the syntax for concatenations in Oracle8i is different from that used in SQL Server 2000, but we can use an escape sequence that represents the concatenation functionality provided by the DBMSs instead of the DBMS syntax. The concatenation example now looks like this:

<cfquery name = "qsEmployees" datasource="Sigal">
SELECT {fn concat (first_name, {fn concat (' ', last_name)})}
AS name
FROM employees
</cfquery>

ODBC escape sequences allow queries to be compatible with several DBMSs while giving access to the functionalities that have been implemented differently by different DBMSs. It's important to note that an escape sequence will only work with a DBMS if the DBMS supports the escape sequence functionality. Escape sequences are not implementations of database functionalities. Rather, they are an alternative way of writing a functionality, an alternative syntax that is translated by the ODBC driver into the DBMS syntax. It's possible to have one version of the queries using this technique. This solution also scales much better than the previous one because the queries are not only compatible with Oracle8i and SQL Server 2000, but also with any other DBMS that has an ODBC driver, in the measure that the DBMS supports the functionality represented by the escape sequences. The learning process is simplified because we don't need to learn the DBMS syntax of different DBMSs once we have learned the ODBC escape sequences. However, using ODBC escape sequences can make the code more complex because of the somewhat ornery syntax. You'll need to plan for a learning period for everyone to become comfortable with the new syntax and when to use it. Despite these mild difficulties, ODBC escape sequences are, in our opinion, the best way to deal with the different ways different DBMSs implement certain functionalities.

Using ODBC Escape Sequences
Let's look at how ODBC escape sequences are used. The functionalities that are implemented differently by different DBMSs are coded using a special syntax, called "escape sequences". Escape sequences have the following structure:

  • They are placed within curly braces so the ODBC driver recognizes them.
  • A code that represents the escape sequence type follows the opening curly brace.
  • The escape sequence content is written.

    The ODBC driver reads the escape sequence and translates it into the DBMS-specific syntax before sending the query to the database. ODBC escape sequences can be used for functions, outer joins, LIKE escape characters, GUIDs, intervals, procedure calls, and date, time, and timestamp literals. For example, an escape sequence function looks like this:

    SELECT {fn MonthName (date_membership)}
    AS month_membership
    FROM employees
    An outer join escape sequence has this syntax:

    SELECT emp.id, ent.name
    FROM {oj employees emp
    LEFT OUTER JOIN entities ent
    ON emp.entity_id = ent.id}
    A date literal has this syntax:

    SELECT COUNT(id) AS num
    FROM employees
    WHERE date_membership > {d '2003-01-01'}
    Challenges
    When we started implementing and testing ODBC escape sequences in our application's queries, we discovered that not all ODBC drivers fully implement the ODBC standard. Some of the functions in the ODBC escape sequence specification were not implemented by our Oracle ODBC driver. For example, we weren't able to use the TimeStampDifference() function with the Oracle ODBC driver. In such cases, we used one of the other two alternatives, conditional statements around different versions of the query, or simple SQL with more processing outside the query.

    Another concern was that by using ODBC escape sequences, we were limiting our database connectivity to ODBC drivers. Our company also develops Java objects to extend the functionality of ColdFusion, and we questioned whether the ODBC escape sequences were a good long-term solution, as Java objects use the JDBC protocol. Research revealed that certain JDBC drivers, known as ODBC bridges, use the ODBC protocol. Also, the JDBC protocol specification includes an escape syntax that is very similar to the ODBC escape sequences. Testing with a pure JDBC driver showed that it was compliant with several escape sequences. We strongly recommend testing with the JDBC driver you're planning to use.

    Conclusion
    Although the process of converting an application's queries to be compatible with a new DBMS can be lengthy, using ODBC escape sequences ensures that most of the work will be done only once. It also preserves the application's performance by allowing access to the database functionality, which has different syntaxes in different DBMSs.

    References

  • ODBC escape sequences: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/ odbcescape_sequences_in_odbc.asp
  • JDBC API: http://java.sun.com/products/jdbc/download.html
  • ColdFusion JRun JDBC driversdocumentation: www.macromedia.com/v1/documents/jr31/jdbcref.pdf
  • In order to post a comment you need to be registered and logged in.

    Register | Sign-in

    Reader Feedback: Page 1 of 1