Selection of a set of rows
in an IBM DB2 Database
with Torque 3.1

The problem with IBM DB2 is the absence of the equivalent of the Oracle ROWNUM feature, at least until OLAP functionalities where made widely available. Torque 3.1 is a neat package, but is not aware of the OLAP functionalities of DB2, which are now enabled even on small systems DB2 UDB packages.

The patch below (you can download it as a separate file by clicking here) fixes this lacking feature, and allows the Torque 3.1 user to use convenient objects like LargeSelect normally.

You can apply it the following way:

  1. Download Torque 3.1 source code from its official distribution site.
  2. Unpack it and change to the src/ subfolder of the source distribution.
  3. Execute patch -p0 < patch-torque-3.1.vido1.txt
  4. You should see something like this:
    patching file java/org/apache/torque/adapter/DBDB2400.java
    patching file java/org/apache/torque/adapter/DBDB2App.java
    patching file java/org/apache/torque/adapter/DBDB2Net.java
    patching file java/org/apache/torque/adapter/DB.java
    patching file java/org/apache/torque/util/BasePeer.java
    
  5. Now all you have to do is remake the Torque 3.1 jar file with ant and voilą.

Enjoy!

diff -Nur java/org/apache/torque/adapter/DBDB2400.java ../../torque-3.1.vido1/src/java/org/apache/torque/adapter/DBDB2400.java
--- java/org/apache/torque/adapter/DBDB2400.java	2003-09-04 14:52:26.000000000 +0900
+++ ../../torque-3.1.vido1/src/java/org/apache/torque/adapter/DBDB2400.java	2004-06-24 19:49:00.000000000 +0900
@@ -58,6 +58,7 @@
  * Torque Database Adapter for DB2/400 on the IBM AS400 platform.
  *
  * @author Scott Weaver
+ * @author Augustin Vidovic
  * @version $Id: DBDB2400.java,v 1.4 2003/01/08 16:43:57 henning Exp $
  */
 public class DBDB2400 extends DBDB2App
@@ -111,4 +112,15 @@
     {
         return new StringBuffer(UCASE + "(").append(in).append(")").toString();
     }
+
+    /**
+     * This method is used to check whether the database supports
+     * limiting the size of the resultset.
+     *
+     * @return LIMIT_STYLE_DB2.
+     */
+    public int getLimitStyle()
+    {
+        return DB.LIMIT_STYLE_DB2;
+    }
 }
diff -Nur java/org/apache/torque/adapter/DBDB2App.java ../../torque-3.1.vido1/src/java/org/apache/torque/adapter/DBDB2App.java
--- java/org/apache/torque/adapter/DBDB2App.java	2003-09-04 14:52:26.000000000 +0900
+++ ../../torque-3.1.vido1/src/java/org/apache/torque/adapter/DBDB2App.java	2004-06-24 19:49:18.000000000 +0900
@@ -66,6 +66,7 @@
  * 
  *
  * @author Hakan Tandogan
+ * @author Augustin Vidovic
  * @version $Id: DBDB2App.java,v 1.5 2002/06/24 18:12:19 mpoeschl Exp $
  */
 public class DBDB2App extends DB
@@ -138,4 +139,15 @@
     public void unlockTable(Connection con, String table) throws SQLException
     {
     }
+
+    /**
+     * This method is used to check whether the database supports
+     * limiting the size of the resultset.
+     *
+     * @return LIMIT_STYLE_DB2.
+     */
+    public int getLimitStyle()
+    {
+        return DB.LIMIT_STYLE_DB2;
+    }
 }
diff -Nur java/org/apache/torque/adapter/DBDB2Net.java ../../torque-3.1.vido1/src/java/org/apache/torque/adapter/DBDB2Net.java
--- java/org/apache/torque/adapter/DBDB2Net.java	2003-09-04 14:52:26.000000000 +0900
+++ ../../torque-3.1.vido1/src/java/org/apache/torque/adapter/DBDB2Net.java	2004-06-24 19:49:46.000000000 +0900
@@ -66,6 +66,7 @@
  * 
  *
  * @author Hakan Tandogan
+ * @author Augustin Vidovic
  * @version $Id: DBDB2Net.java,v 1.5 2002/06/24 18:12:19 mpoeschl Exp $
  */
 public class DBDB2Net
@@ -139,4 +140,15 @@
     public void unlockTable(Connection con, String table) throws SQLException
     {
     }
+
+    /**
+     * This method is used to check whether the database supports
+     * limiting the size of the resultset.
+     *
+     * @return LIMIT_STYLE_DB2.
+     */
+    public int getLimitStyle()
+    {
+        return DB.LIMIT_STYLE_DB2;
+    }
 }
diff -Nur java/org/apache/torque/adapter/DB.java ../../torque-3.1.vido1/src/java/org/apache/torque/adapter/DB.java
--- java/org/apache/torque/adapter/DB.java	2003-09-04 14:52:26.000000000 +0900
+++ ../../torque-3.1.vido1/src/java/org/apache/torque/adapter/DB.java	2004-06-24 19:46:19.000000000 +0900
@@ -88,6 +88,7 @@
  * @author Jon S. Stevens
  * @author Brett McLaughlin
  * @author Daniel Rall
+ * @author Augustin Vidovic
  * @version $Id: DB.java,v 1.31 2003/01/08 16:43:57 henning Exp $
  */
 public abstract class DB implements Serializable, IDMethod
@@ -107,6 +108,9 @@
     /** 
SELECT ... WHERE ... AND ROWNUM < 
*/ public static final int LIMIT_STYLE_ORACLE = 4; + /**
SELECT ... WHERE ... AND ROW_NUMBER() OVER() < 
*/ + public static final int LIMIT_STYLE_DB2 = 5; + /** * Empty constructor. */ diff -Nur java/org/apache/torque/util/BasePeer.java ../../torque-3.1.vido1/src/java/org/apache/torque/util/BasePeer.java --- java/org/apache/torque/util/BasePeer.java 2003-09-04 14:52:26.000000000 +0900 +++ ../../torque-3.1.vido1/src/java/org/apache/torque/util/BasePeer.java 2004-06-24 19:58:45.000000000 +0900 @@ -110,6 +110,7 @@ * @author Brett McLaughlin * @author Stephen Haberman * @author Martin Poeschl + * @author Augustin Vidovic * @version $Id: BasePeer.java,v 1.76 2003/08/25 16:33:22 henning Exp $ */ public abstract class BasePeer implements java.io.Serializable @@ -971,6 +972,53 @@ } /** + * Build DB2 (OLAP) -style query with limit or offset. + * If the original SQL is in variable: query then the requlting + * SQL looks like this: + *
+     * SELECT B.* FROM (
+     *          SELECT A.*, row_number() over() as TORQUE$ROWNUM FROM (
+     *                  query
+     *          ) A
+     *     ) B WHERE B.TORQUE$ROWNUM > offset AND B.TORQUE$ROWNUM
+     *     <= offset + limit
+     * 
+ * + * @param query the query + * @param limit + * @param offset + * @return oracle-style query + */ + private static String createDB2LimitOffsetQuery(Query query, + int limit, int offset) + { + StringBuffer buf = new StringBuffer(); + buf.append("SELECT B.* FROM ( "); + buf.append("SELECT A.*, row_number() over() AS TORQUE$ROWNUM FROM ( "); + + buf.append(query.toString()); + buf.append(" ) A "); + buf.append(" ) B WHERE "); + + if (offset > 0) + { + buf.append(" B.TORQUE$ROWNUM > "); + buf.append(offset); + if (limit > 0) + { + buf.append(" AND B.TORQUE$ROWNUM <= "); + buf.append(offset + limit); + } + } + else + { + buf.append(" B.TORQUE$ROWNUM <= "); + buf.append(limit); + } + return buf.toString(); + } + + /** * Method to create an SQL query for actual execution based on values in a * Criteria. * @@ -988,13 +1036,18 @@ int limit = criteria.getLimit(); int offset = criteria.getOffset(); - String sql; - if ((limit > 0 || offset > 0) - && db.getLimitStyle() == DB.LIMIT_STYLE_ORACLE) + String sql=null; + if (limit > 0 || offset > 0) { + if (db.getLimitStyle() == DB.LIMIT_STYLE_ORACLE) { sql = createOracleLimitOffsetQuery(query, limit, offset); criteria.setLimit(-1); criteria.setOffset(0); + } else if (db.getLimitStyle() == DB.LIMIT_STYLE_DB2) { + sql = createDB2LimitOffsetQuery(query, limit, offset); + criteria.setLimit(-1); + criteria.setOffset(0); + } } else { @@ -2549,15 +2602,20 @@ } } - String sql; - if ((limit > 0 || offset > 0) - && db.getLimitStyle() == DB.LIMIT_STYLE_ORACLE) + String sql=null; + if (limit > 0 || offset > 0) { + if ( db.getLimitStyle() == DB.LIMIT_STYLE_ORACLE) { sql = createOracleLimitOffsetQuery(query, limit, offset); criteria.setLimit(-1); criteria.setOffset(0); + } else if ( db.getLimitStyle() == DB.LIMIT_STYLE_DB2) { + sql = createDB2LimitOffsetQuery(query, limit, offset); + criteria.setLimit(-1); + criteria.setOffset(0); + } } - else + else { sql = query.toString(); }