This is an automated email from the ASF dual-hosted git repository.

doebele pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/empire-db.git


The following commit(s) were added to refs/heads/master by this push:
     new 1d3600b  EMPIREDB-362 bugfix DBBeanListFactoryImpl
1d3600b is described below

commit 1d3600bb21fe7eb9b3c60d9cd4740c0c2bf66db2
Author: Rainer Döbele <[email protected]>
AuthorDate: Sun Feb 27 02:41:43 2022 +0100

    EMPIREDB-362 bugfix DBBeanListFactoryImpl
---
 .../empire/samples/db/advanced/HtmlGenUtil.java    | 217 ++++++++++---------
 .../empire-db-example-basic/config.xml             |   8 +-
 .../empire/db/list/DBBeanListFactoryImpl.java      |  38 +++-
 .../org/apache/empire/dbms/hsql/DBCommandHSql.java | 233 +++++++++++++++++++++
 .../apache/empire/dbms/hsql/DBMSHandlerHSql.java   |  18 +-
 .../apache/empire/dbms/oracle/DBCommandOracle.java |  18 +-
 6 files changed, 419 insertions(+), 113 deletions(-)

diff --git 
a/empire-db-examples/empire-db-example-advanced/src/main/java/org/apache/empire/samples/db/advanced/HtmlGenUtil.java
 
b/empire-db-examples/empire-db-example-advanced/src/main/java/org/apache/empire/samples/db/advanced/HtmlGenUtil.java
index bdd1e1a..5ab8c61 100644
--- 
a/empire-db-examples/empire-db-example-advanced/src/main/java/org/apache/empire/samples/db/advanced/HtmlGenUtil.java
+++ 
b/empire-db-examples/empire-db-example-advanced/src/main/java/org/apache/empire/samples/db/advanced/HtmlGenUtil.java
@@ -22,6 +22,7 @@ import org.apache.empire.commons.StringUtils;
 import org.apache.empire.db.DBColumn;
 import org.apache.empire.db.DBDatabase;
 import org.apache.empire.db.DBTable;
+import org.apache.empire.exceptions.ItemNotFoundException;
 
 /**
  * Temporary class for HTML-generation of code and SQL
@@ -46,7 +47,7 @@ public class HtmlGenUtil
             "// Returns a list of Java beans (needs matching fields 
constructor or setter methods)           \r\n" + 
             "// This is just one of several options to obtain an process query 
results          \r\n" + 
             "List<QueryResult> list = context.getUtils().queryBeanList(cmd, 
QueryResult.class, null);\r\n" + 
-            "log.info(\"queryBeanList returnes {} items\", list.size());"; 
+            "log.info(\"queryBeanList returned {} items\", list.size());"; 
     
     public static final String codePriceUpdate = "// create command\r\n" + 
             "DBCommand cmd = context.createCommand()\r\n" + 
@@ -74,9 +75,29 @@ public class HtmlGenUtil
             "CodeGenerator app = new CodeGenerator();\r\n" + 
             "app.generate(dbms, conn, config);\r\n";
     
+    public static final Object[] codeRecordReadLiterals = new Object[] { 55, 
2021, 12, "Anna", "Smith" };
+    public static final String codeRecordRead="DBRecord employee = new 
DBRecord(context, db.EMPLOYEES);\r\n" + 
+            "SampleDB.Employees EMP = db.EMPLOYEES;\r\n" + 
+            "// read record with identity column primary key\r\n" + 
+            "employee.read(55);\r\n" + 
+            "// read record with multiple column primary key \r\n" + 
+            "payment.read(DBRecord.key(55, 2021, 12));\r\n" + 
+            "// read with constraints \r\n" + 
+            
"employee.read(EMP.FIRST_NAME.is(\"Anna\").and(EMP.LAST_NAME.is(\"Smith\")));\r\n"
 + 
+            "// read record identified by a subquery\r\n" + 
+            "DBCommand sel = context.createCommand();\r\n" + 
+            "sel.select(db.PAYMENTS.EMPLOYEE_ID);\r\n" + 
+            "sel.where(/* some constraints */);\r\n" + 
+            "employee.read(EMP.ID.is(sel));\r\n" + 
+            "// read record partially with only 3 columns\r\n" + 
+            "employee.read(DBRecord.key(55), PartialMode.INCLUDE, 
EMP.FIRST_NAME, EMP.LAST_NAME, EMP.SALARY);\r\n";
+    
     public static String codeToHtml(DBDatabase db, String code, Object... 
literals)
     {
         code = prepareHtml(code);
+        // comment
+        code = replaceFragment(code, "/*", "*/", "<span class=\"comment\">", 
"</span>");
+        code = replaceFragment(code, "//", "\r\n", "<span class=\"comment\">", 
"</span>");
         // replace literals
         for (int i=0; i<literals.length; i++)
         {
@@ -85,35 +106,43 @@ public class HtmlGenUtil
                 literal = "\""+((String)literals[i])+"\"";
             else
                 literal = String.valueOf(literals[i]);
-            code = replaceWord(code, literal, ' ', "<span class=\"literal\">", 
"</span>");
+            code = replaceWord(code, literal, false, "<span 
class=\"literal\">", "</span>");
         }
         // null
-        code = replaceWord(code, "null", ' ', "<span class=\"literal\">", 
"</span>");
-        code = replaceWord(code, "new", ' ', "<span class=\"new\">", 
"</span>");
+        code = replaceWord(code, "null", false, "<span class=\"literal\">", 
"</span>");
+        code = replaceWord(code, "new",  false, "<span class=\"keyword\">", 
"</span>");
+        code = replaceWord(code, ".class", true, "<span class=\"keyword\">", 
"</span>");
         // types
-        String[] types = new String[] { "CarSalesDB", "CodeGenerator", 
"CodeGenConfig", "DBMSHandlerOracle", "DBMSHandler ", "Connection ", 
"DBCommand", "QueryResult", "EngineType", "int ", "long ", "String " };
+        String[] types = new String[] { "int ", "CarSalesDB", "Connection ", 
"DBUtils", "DBCommand", "DBRecord", "PartialMode", "EngineType", "long ", 
"String ", "List<", "QueryResult" };
         for (int i=0; i<types.length; i++)
-            code = replaceWord(code, types[i], ' ', "<span class=\"type\">", 
"</span>");
+            code = replaceWord(code, types[i], true, "<span class=\"type\">", 
"</span>");
+        // variables
+        String[] variables = new String[] { "db_XXX", "context", "cmd", 
"record", "utils", "employee", "list", "result"};
+        for (int i=0; i<variables.length; i++)
+            code = replaceWord(code, variables[i], false, "<span 
class=\"var\">", "</span>");
         // Tables and columns
         if (db!=null)
         {   for (DBTable t : db.getTables())
             {
-                code = replaceWord(code, t.getName(), ' ', "<span 
class=\"obj\">", "</span>");
+                String table = t.getName();
+                code = replaceWord(code, table, false, "<span class=\"obj\">", 
"</span>");
+                code = replaceWord(code, table.substring(0,3)+"_XXX", false, 
"<span class=\"obj\">", "</span>");
                 for (DBColumn c : t.getColumns())
                 {
-                    code = replaceWord(code, "."+c.getName(), '.', "<span 
class=\"var\">", "</span>");
+                    code = replaceWord(code, "."+c.getName(), true, "<span 
class=\"field\">", "</span>");
                 }
             }
         }
         // functions
-        code = replaceFragment(code, '.', '(', false, "<span class=\"func\">", 
"</span>", new char[] { 'a', 'z' });
-        // literals
-        return replaceComment(code, "<span class=\"comment\">", "</span>"); 
+        code = replaceFunction(code, '.', '(', "<span class=\"func\">", 
"</span>");
+        // shorten
+        return code.replace("_XXX", "");
     }
     
-    public static String sqlToHtml(DBDatabase db, String sql, Number... 
literals)
+    public static String sqlToHtml(DBDatabase db, String sql, Object... 
literals)
     {
         sql = prepareHtml(sql);
+        sql = sql.replace("N'", "'");
         /*
         UPDATE t2
         SET BASE_PRICE=round(t2.BASE_PRICE*105/100,0)
@@ -122,19 +151,25 @@ public class HtmlGenUtil
         */
         String[] words = new String[] { "SELECT ", "UPDATE ", "INSERT ", "SET" 
, "FROM ", "WHERE ", "GROUP BY ", "HAVING ", "ORDER BY", " IN ", " ON ", " AND 
", " INNER JOIN ", " LEFT JOIN ", " RIGHT_JOIN " };
         for (int i=0; i<words.length; i++)
-            sql = replaceWord(sql, words[i], ' ', "<span class=\"word\">", 
"</span>");
+            sql = replaceWord(sql, words[i], true, "<span class=\"word\">", 
"</span>");
         for (DBTable t : db.getTables())
-            sql = replaceWord(sql, t.getAlias(), ' ', "<span 
class=\"alias\">", "</span>");
+            sql = replaceWord(sql, t.getAlias(), false, "<span 
class=\"alias\">", "</span>");
         // functions
-        String[] func = new String[] { "count", "round", "avg" };
+        String[] func = new String[] { "count(", "round(", "avg(" };
         for (int i=0; i<func.length; i++)
-            sql = replaceWord(sql, func[i], ' ', "<span class=\"func\">", 
"</span>");
+            sql = replaceWord(sql, func[i], true, "<span class=\"func\">", 
"</span>");
         // finally literals
         for (int i=0; i<literals.length; i++)
-            sql = replaceWord(sql, literals[i].toString(), ' ', "<span 
class=\"literal\">", "</span>");
-        // String literals
-        sql = sql.replace("N'", "'");
-        return replaceFragment(sql, '\'', '\'', true, "<span 
class=\"param\"><span class=\"literal\">", "</span></span>", null);
+        {
+            String literal;
+            if (literals[i] instanceof String)
+                literal = "'"+((String)literals[i])+"'";
+            else
+                literal = String.valueOf(literals[i]);
+            sql = replaceWord(sql, literal, false, "<span 
class=\"param\"><span class=\"literal\">", "</span></span>");
+        }
+        // done
+        return sql;
     }
     
     /*
@@ -148,24 +183,53 @@ public class HtmlGenUtil
         str = StringUtils.replace(str, "<", "&lt;");
         return StringUtils.replace(str, ">", "&gt;");
     }
+
+    public static boolean isCommentLine(String str, int i) 
+    {
+        for (;i>0;i--)
+        {
+            char c = str.charAt(i);
+            if (c=='\n')
+                break;
+            if (c=='/' && str.charAt(i-1)=='/')
+                return true;
+        }
+        return false;
+    }
+
+    public static char specialChar(char c) 
+    {
+        if ((c>='a' && c<='z') || (c>='A' && c<='Z'))
+            return 0;
+        return c;
+    }
     
-    public static String replaceWord(String str, String word, char intro, 
String htmlBeg, String htmlEnd) 
+    public static String replaceWord(String str, String word, boolean special, 
String htmlBeg, String htmlEnd) 
     {
+        char intro = (special ? specialChar(word.charAt(0)) : 0); 
+        char extro = (special ? specialChar(word.charAt(word.length()-1)) : 
0); 
         // not present
         if (str.indexOf(word)<0)
             return str;
         // replace
-        String wtrim = word.trim();
-        if (wtrim.charAt(0)==intro)
+        String wtrim = word;
+        if (intro>0)
             wtrim = wtrim.substring(1);
+        if (extro>0)
+            wtrim = wtrim.substring(0, wtrim.length()-1);
         StringBuilder s = new StringBuilder();
         int i = 0;
         int p = 0;
         while ((i=str.indexOf(word, p))>=0)
         {
-            if (word.charAt(0)==intro)
-                i++;
-            s.append(str.substring(p, i));
+            if (isCommentLine(str, i))
+            {   // comment: ignore
+                i += word.length();
+                s.append(str.substring(p, i));
+                p = i;
+                continue;
+            }
+            s.append(str.substring(p, (intro>0) ? ++i : i));
             s.append(htmlBeg);
             s.append(wtrim);
             s.append(htmlEnd);
@@ -176,7 +240,7 @@ public class HtmlGenUtil
         return s.toString();
     }
     
-    public static String replaceFragment(String str, char beg, char end, 
boolean include, String htmlBeg, String htmlEnd, char[] nextRange) 
+    public static String replaceFunction(String str, char beg, char end, 
String htmlBeg, String htmlEnd) 
     {
         StringBuilder s = new StringBuilder();
         int i = 0;
@@ -184,50 +248,49 @@ public class HtmlGenUtil
         while ((i=str.indexOf(beg, p))>=0)
         {
             // function special
-            if (nextRange!=null)
-            {   // check range of next char
-                char next = str.charAt(i+1);
-                if (next<nextRange[0] || next>nextRange[1])
-                {   // ignore
-                    s.append(str.substring(p, ++i));
-                    p = i;
-                    continue;
-                }
+            int j = ++i;
+            while(true)
+            {
+                char c = str.charAt(j);
+                boolean ok = (c>='a' && c<='z') || (c>='A' && c<='Z');
+                if (!ok)
+                    break;
+                j++;
             }
-            int n = str.indexOf('\n', i+1);
-            int j = str.indexOf(end,  ++i);
-            if (n<j)
-            {   // line-break: ignore
-                s.append(str.substring(p, n+1));
-                p = n+1;
+            // skip whitespace
+            int k = j;
+            while (str.charAt(k)==' ')
+                k++;
+            // check end
+            if (str.charAt(k)!=end)
+            {   // not found, something else
+                s.append(str.substring(p, j));
+                p = j;
                 continue;
             }
-            if (!include)
-            {   // remove whitespace
-                while(str.charAt(j-1)==' ') j--;
-            }
-            int o = (include) ? 1 : 0; 
-            s.append(str.substring(p, i-o));
+            s.append(str.substring(p, i));
             s.append(htmlBeg);
-            s.append(str.substring(i-o, j+o));
+            s.append(str.substring(i, j));
             s.append(htmlEnd);
             // next
-            p = j + o;
+            p = j;
         }
         s.append(str.substring(p));
         return s.toString();
     }
     
-    public static String replaceComment(String str, String htmlBeg, String 
htmlEnd) 
+    public static String replaceFragment(String str, String beg, String end, 
String htmlBeg, String htmlEnd) 
     {
         StringBuilder s = new StringBuilder();
-        String beg = "//";
-        String end ="\r\n";
         int i = 0;
         int p = 0;
         while ((i=str.indexOf(beg, p))>=0)
         {
             int j = str.indexOf(end, i+1);
+            if (j<0)
+                throw new ItemNotFoundException(end);
+            if (!end.equals("\r\n"))
+                j+= end.length();
             s.append(str.substring(p, i));
             s.append(htmlBeg);
             s.append(str.substring(i, j));
@@ -238,53 +301,5 @@ public class HtmlGenUtil
         s.append(str.substring(p));
         return s.toString();
     }
-    
-    /*
-    public static String replaceSqlWord(String sql, String word, String 
htmlBeg, String htmlEnd) 
-    {
-        // not present
-        if (sql.indexOf(word)<0)
-            return sql;
-        // replace
-        String wtrim = word.trim();
-        StringBuilder s = new StringBuilder();
-        int i = 0;
-        int p = 0;
-        while ((i=sql.indexOf(word, p))>=0)
-        {
-            if (word.charAt(0)==' ')
-                i++;
-            s.append(sql.substring(p, i));
-            s.append(htmlBeg);
-            s.append(wtrim);
-            s.append(htmlEnd);
-            // next
-            p = i + wtrim.length();
-        }
-        s.append(sql.substring(p));
-        return s.toString();
-    }
-    */
-    
-    /*
-    public static String replaceSqlStringLiteral(String sql, String htmlBeg, 
String htmlEnd) 
-    {
-        StringBuilder s = new StringBuilder();
-        int i = 0;
-        int p = 0;
-        while ((i=sql.indexOf('\'', p))>=0)
-        {
-            int j = sql.indexOf('\'', ++i);
-            s.append(sql.substring(p, i-1));
-            s.append(htmlBeg);
-            s.append(sql.substring(i-1, j+1));
-            s.append(htmlEnd);
-            // next
-            p = j + 1;
-        }
-        s.append(sql.substring(p));
-        return s.toString();
-    }
-    */
 
 }
diff --git a/empire-db-examples/empire-db-example-basic/config.xml 
b/empire-db-examples/empire-db-example-basic/config.xml
index 44955f1..3e6bfee 100644
--- a/empire-db-examples/empire-db-example-basic/config.xml
+++ b/empire-db-examples/empire-db-example-basic/config.xml
@@ -21,7 +21,7 @@
 
        <properties>
                <!-- provider name must match the property-section containing 
the connection data -->
-               <databaseProvider>sqlserver</databaseProvider>
+               <databaseProvider>hsqldb</databaseProvider>
        </properties>
 
        <properties-hsqldb>
@@ -133,6 +133,12 @@
                        </layout>
                </appender>
        
+               <appender name="sample" 
class="org.apache.log4j.ConsoleAppender">
+                       <layout class="org.apache.log4j.PatternLayout">
+                               <param name="ConversionPattern" value="%-5p : 
*** %m *** %n"/>
+                       </layout>
+               </appender>
+       
                <!-- log detail configuration -->
                <logger name="org.apache.empire.xml" additivity="false">
                        <level value="info"/>
diff --git 
a/empire-db/src/main/java/org/apache/empire/db/list/DBBeanListFactoryImpl.java 
b/empire-db/src/main/java/org/apache/empire/db/list/DBBeanListFactoryImpl.java
index 8676cef..1724946 100644
--- 
a/empire-db/src/main/java/org/apache/empire/db/list/DBBeanListFactoryImpl.java
+++ 
b/empire-db/src/main/java/org/apache/empire/db/list/DBBeanListFactoryImpl.java
@@ -23,6 +23,7 @@ import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.commons.beanutils.MethodUtils;
 import org.apache.empire.commons.ClassUtils;
 import org.apache.empire.commons.ObjectUtils;
 import org.apache.empire.commons.StringUtils;
@@ -73,10 +74,24 @@ public class DBBeanListFactoryImpl<T> implements 
DBBeanListFactory<T>
         return ClassUtils.findMatchingConstructor(beanType, 0);
     }
     
+    protected static Class<?>[] getParameterTypes(Constructor<?> constructor)
+    {
+        if (constructor==null)
+            return null;
+        Class<?>[] parameterTypes = constructor.getParameterTypes();
+        for (int i=0; i<parameterTypes.length; i++)
+        {   // get wrapper for primitive types
+            if (parameterTypes[i].isPrimitive())
+                parameterTypes[i] = 
MethodUtils.getPrimitiveWrapper(parameterTypes[i]);
+        }
+        return parameterTypes;
+    }
+    
     /*
      * Members
      */
     protected final Constructor<T> constructor;
+    protected final Class<?>[] parameterTypes;
     protected final List<? extends DBColumnExpr> constructorParams;
     protected final List<? extends DBColumnExpr> setterColumns;
     
@@ -89,6 +104,7 @@ public class DBBeanListFactoryImpl<T> implements 
DBBeanListFactory<T>
     public DBBeanListFactoryImpl(Constructor<T> constructor, List<? extends 
DBColumnExpr> constructorParams, List<? extends DBColumnExpr> setterColumns) 
     {
         this.constructor = constructor;
+        this.parameterTypes = getParameterTypes(constructor);
         this.constructorParams = constructorParams;
         this.setterColumns = setterColumns;
         // Check constructor
@@ -122,6 +138,7 @@ public class DBBeanListFactoryImpl<T> implements 
DBBeanListFactory<T>
             this.setterColumns = selectColumns;
         }
         this.constructor = constructor;
+        this.parameterTypes = getParameterTypes(constructor);
         // log
         if (constructor!=null && log.isDebugEnabled())
             log.debug("{}: using bean constructor with {} params", 
beanType.getName(), constructor.getParameterCount());
@@ -157,6 +174,7 @@ public class DBBeanListFactoryImpl<T> implements 
DBBeanListFactory<T>
         }
         // found one
         this.constructor = constructor;
+        this.parameterTypes = getParameterTypes(constructor);
         // log
         if (constructor!=null && log.isDebugEnabled())
             log.debug("{}: using bean constructor with {} params", 
beanType.getName(), constructor.getParameterCount());
@@ -225,12 +243,26 @@ public class DBBeanListFactoryImpl<T> implements 
DBBeanListFactory<T>
                 {
                     Class<Enum<?>> enumType = expr.getEnumType();
                     if (enumType!=null)
-                        params[i++] = recData.getEnum(expr, enumType);
+                        params[i] = recData.getEnum(expr, enumType);
                     else
-                        params[i++] = recData.get(expr);
+                        params[i] = recData.get(expr);
+                    // check type
+                    if (params[i]!=null)
+                    {   // compare types
+                        Class<?> valueType = params[i].getClass();
+                        if (!parameterTypes[i].isAssignableFrom(valueType))
+                        {   // Param type does not match
+                            if (log.isDebugEnabled())
+                                log.debug("{} type of param {} doesn't match: 
expected \"{}\" got \"{}\"", constructor.getDeclaringClass().getName(), i, 
parameterTypes[i].getName(), valueType.getName());
+                            // convert
+                            params[i] = ObjectUtils.convert(parameterTypes[i], 
params[i]);
+                        }
+                    }
                     // log
                     if (log.isTraceEnabled())
-                        log.trace("{}: constructor param '{}' is {}", 
constructor.getDeclaringClass().getName(), StringUtils.coalesce(expr.getName(), 
String.valueOf(i-1)), params[i-1]);
+                        log.trace("{}: constructor param '{}' is {}", 
constructor.getDeclaringClass().getName(), StringUtils.coalesce(expr.getName(), 
String.valueOf(i)), params[i]);
+                    // next
+                    i++;
                 }
                 // create item
                 bean = constructor.newInstance(params);
diff --git 
a/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBCommandHSql.java 
b/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBCommandHSql.java
new file mode 100644
index 0000000..97cace1
--- /dev/null
+++ b/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBCommandHSql.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.dbms.hsql;
+
+import java.util.ArrayList;
+// Imports
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.empire.data.DataType;
+import org.apache.empire.db.DBColumn;
+import org.apache.empire.db.DBColumnExpr;
+import org.apache.empire.db.DBCommand;
+import org.apache.empire.db.DBRowSet;
+import org.apache.empire.db.expr.column.DBAliasExpr;
+import org.apache.empire.db.expr.column.DBValueExpr;
+import org.apache.empire.db.expr.compare.DBCompareColExpr;
+import org.apache.empire.db.expr.compare.DBCompareExpr;
+import org.apache.empire.db.expr.join.DBColumnJoinExpr;
+import org.apache.empire.db.expr.join.DBJoinExpr;
+import org.apache.empire.db.expr.set.DBSetExpr;
+import org.apache.empire.exceptions.ObjectNotValidException;
+
+/**
+ * This class handles the special features of an HSqlDB database.
+ */
+public class DBCommandHSql extends DBCommand
+{
+    // *Deprecated* private static final long serialVersionUID = 1L;
+  
+    protected int limitRows = -1;
+    protected int skipRows  =  0;
+
+    /**
+     * Constructs an HSqlDB command object.
+     */
+    public DBCommandHSql(boolean autoPrepareStmt)
+    {
+        super(autoPrepareStmt);
+    }
+
+    @Override
+    public DBCommandHSql limitRows(int limitRows)
+    {
+        // set limit
+        this.limitRows = limitRows;
+        return this;
+    }
+
+    @Override
+    public DBCommandHSql skipRows(int skipRows)
+    {
+        // set skip
+        this.skipRows = skipRows;
+        return this;
+    }
+     
+    @Override
+    public void clearLimit()
+    {
+       // remove skip and limit
+        this.limitRows = -1;
+        this.skipRows  =  0;
+    }
+        
+    @Override
+    public void getSelect(StringBuilder buf)
+    {   // call base class
+        super.getSelect(buf);
+        // add limit and offset
+        if (limitRows>=0)
+        {   buf.append("\r\nLIMIT ");
+            buf.append(String.valueOf(limitRows));
+            // Offset
+            if (skipRows>0) 
+            {   buf.append(" OFFSET ");
+                buf.append(String.valueOf(skipRows));
+            }    
+        }
+    }
+    
+    /**
+     * Creates an update statement.
+     * If a join is required, this method creates a "MERGE INTO" expression 
+     */
+    @Override
+    public synchronized String getUpdate()
+    {
+        // No Joins: Use Default
+        if (joins==null || set==null)
+            return super.getUpdate();
+        else
+            return getUpdateWithJoins();
+    }
+    
+    protected String getUpdateWithJoins()
+    {
+        // Generate Merge expression
+        resetParamUsage();
+        StringBuilder buf = new StringBuilder("MERGE INTO ");
+        DBRowSet table =  set.get(0).getTable();
+        table.addSQL(buf, CTX_FULLNAME|CTX_ALIAS);
+        // join (only one allowed yet)
+        DBColumnJoinExpr updateJoin = null;
+        for (DBJoinExpr jex : joins)
+        {   // The join
+            if (!(jex instanceof DBColumnJoinExpr))
+                continue;
+            if (jex.isJoinOn(table)==false)
+                continue;
+            // found the join
+            updateJoin = (DBColumnJoinExpr)jex;
+            break;
+        }
+        if (updateJoin==null)
+            throw new ObjectNotValidException(this);
+        Set<DBColumn> joinColumns = new HashSet<DBColumn>();
+        updateJoin.addReferencedColumns(joinColumns);
+        // using
+        buf.append("\r\nUSING ");
+        DBCommand inner = this.clone();
+        inner.clearSelect();
+        inner.clearOrderBy();
+        DBRowSet outerTable = updateJoin.getOuterTable();
+        if (outerTable==null)
+            outerTable=table;
+        for (DBColumn jcol : joinColumns)
+        {   // Select join columns
+            if (jcol.getRowSet().equals(outerTable)==false)
+                inner.select(jcol);
+        }
+        // find the source table
+        DBColumnExpr left  = updateJoin.getLeft();
+        DBColumnExpr right = updateJoin.getRight();
+        DBRowSet source = right.getUpdateColumn().getRowSet();
+        if (source==table)
+            source = left.getUpdateColumn().getRowSet();
+        // Add set expressions
+        String sourceAliasPrefix = source.getAlias()+".";
+        List<DBSetExpr> mergeSet = new ArrayList<DBSetExpr>(set.size());   
+        for (DBSetExpr sex : set)
+        {   // Select set expressions
+            Object val = sex.getValue();
+            if (val instanceof DBColumnExpr)
+            {
+                DBColumnExpr expr = ((DBColumnExpr)val);
+                if (!(expr instanceof DBColumn) && !(expr instanceof 
DBAliasExpr))
+                {   // rename column
+                    String name = "COL_"+String.valueOf(mergeSet.size());
+                    expr = expr.as(name);
+                }
+                // select
+                inner.select(expr);
+                // Name
+                DBValueExpr NAME_EXPR = 
getDatabase().getValueExpr(sourceAliasPrefix+expr.getName(), DataType.UNKNOWN);
+                mergeSet.add(sex.getColumn().to(NAME_EXPR));
+            }
+            else
+            {   // add original
+                mergeSet.add(sex);
+            }
+        }
+        // remove join (if not necessary)
+        if (inner.hasConstraintOn(table)==false)
+            inner.removeJoinsOn(table);
+        // add SQL for inner statement
+        inner.addSQL(buf, CTX_DEFAULT);
+        // add Alias
+        buf.append(" ");
+        buf.append(source.getAlias());
+        buf.append("\r\nON (");
+        left.addSQL(buf, CTX_DEFAULT);
+        buf.append(" = ");
+        right.addSQL(buf, CTX_DEFAULT);
+        // Compare Expression
+        if (updateJoin.getWhere() != null)
+        {   buf.append(" AND ");
+            updateJoin.getWhere().addSQL(buf, CTX_DEFAULT);
+        }
+        // More constraints
+        for (DBCompareExpr we : this.where) 
+        {
+            if (we instanceof DBCompareColExpr)
+            {   // a compare column expression
+                DBCompareColExpr cce = (DBCompareColExpr)we;
+                DBColumn ccecol = cce.getColumn().getUpdateColumn();
+                if (table.isKeyColumn(ccecol)&& !isSetColumn(ccecol))  
+                {
+                    buf.append(" AND ");
+                    cce.addSQL(buf, CTX_DEFAULT);
+                }
+            }
+            else
+            {   // just add
+                buf.append(" AND ");
+                we.addSQL(buf, CTX_DEFAULT);
+            }
+        }
+        // Set Expressions
+        buf.append(")\r\nWHEN MATCHED THEN UPDATE ");
+        buf.append("\r\nSET ");
+        addListExpr(buf, mergeSet, CTX_DEFAULT, ", ");
+        // done
+        return buf.toString();
+    }
+        
+    protected boolean isSetColumn(DBColumn col)
+    {
+        for (DBSetExpr se : this.set)
+        {
+            if (se.getColumn().equals(col))
+                return true;
+        }
+        return false;
+    }
+}
diff --git 
a/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBMSHandlerHSql.java 
b/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBMSHandlerHSql.java
index b4c0825..494e3ed 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBMSHandlerHSql.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBMSHandlerHSql.java
@@ -24,6 +24,7 @@ import java.util.GregorianCalendar;
 import org.apache.empire.commons.ObjectUtils;
 import org.apache.empire.data.DataType;
 import org.apache.empire.db.DBColumnExpr;
+import org.apache.empire.db.DBCommand;
 import org.apache.empire.db.DBDDLGenerator;
 import org.apache.empire.db.DBDDLGenerator.DDLActionType;
 import org.apache.empire.db.DBDatabase;
@@ -72,14 +73,25 @@ public class DBMSHandlerHSql extends DBMSHandlerBase
     public boolean isSupported(DBMSFeature type)
     {
         switch (type)
-        {   // return support info 
-            case CREATE_SCHEMA: return false;
-            case SEQUENCES:     return true;    
+        {   // return support info
+            case CREATE_SCHEMA:     return false;
+            case SEQUENCES:         return true;
+            case QUERY_LIMIT_ROWS:  return true;
+            case QUERY_SKIP_ROWS:   return true;
             default:
                 // All other features are not supported by default
                 return false;
         }
     }
+
+    /**
+     * Override standard command
+     */
+    @Override
+    public DBCommand createCommand(boolean autoPrepareStmt)
+    {
+        return new DBCommandHSql(autoPrepareStmt);
+    }
     
     /**
      * Gets an sql phrase template for this database system.<br>
diff --git 
a/empire-db/src/main/java/org/apache/empire/dbms/oracle/DBCommandOracle.java 
b/empire-db/src/main/java/org/apache/empire/dbms/oracle/DBCommandOracle.java
index a8c1406..b7cf772 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/oracle/DBCommandOracle.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/oracle/DBCommandOracle.java
@@ -373,12 +373,20 @@ public class DBCommandOracle extends DBCommand
         // More constraints
         for (DBCompareExpr we : this.where) 
         {
-            DBCompareColExpr cce = (DBCompareColExpr)we;
-            DBColumn ccecol = cce.getColumn().getUpdateColumn();
-            if (table.isKeyColumn(ccecol)&& !isSetColumn(ccecol))  
-            {
+            if (we instanceof DBCompareColExpr)
+            {   // a compare column expression
+                DBCompareColExpr cce = (DBCompareColExpr)we;
+                DBColumn ccecol = cce.getColumn().getUpdateColumn();
+                if (table.isKeyColumn(ccecol)&& !isSetColumn(ccecol))  
+                {
+                    buf.append(" AND ");
+                    cce.addSQL(buf, CTX_DEFAULT);
+                }
+            }
+            else
+            {   // just add
                 buf.append(" AND ");
-                cce.addSQL(buf, CTX_DEFAULT);
+                we.addSQL(buf, CTX_DEFAULT);
             }
         }
         // Set Expressions

Reply via email to