You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@empire-db.apache.org by do...@apache.org on 2022/08/21 18:36:25 UTC

[empire-db] branch master updated: EMPIREDB-394 cleanup DBMSHandler

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 0a54b99a EMPIREDB-394 cleanup DBMSHandler
0a54b99a is described below

commit 0a54b99ab7dc660e172a412d26bba2a0d42359ec
Author: Rainer Döbele <do...@apache.org>
AuthorDate: Sun Aug 21 20:36:23 2022 +0200

    EMPIREDB-394 cleanup DBMSHandler
---
 .../java/org/apache/empire/db/DBDDLGenerator.java  |   2 +-
 .../java/org/apache/empire/db/DBSQLBuilder.java    | 337 +++++++++++++++++----
 .../apache/empire/db/expr/column/DBValueExpr.java  |  22 +-
 .../java/org/apache/empire/dbms/DBMSHandler.java   |  12 -
 .../org/apache/empire/dbms/DBMSHandlerBase.java    | 220 --------------
 .../empire/dbms/derby/DerbyDDLGenerator.java       |   2 +-
 .../apache/empire/dbms/mysql/DBMSHandlerMySQL.java |  75 +++--
 .../dbms/postgresql/PostgresDDLGenerator.java      |   2 +-
 .../empire/dbms/sqlserver/DBMSHandlerMSSQL.java    |  65 ++--
 9 files changed, 380 insertions(+), 357 deletions(-)

diff --git a/empire-db/src/main/java/org/apache/empire/db/DBDDLGenerator.java b/empire-db/src/main/java/org/apache/empire/db/DBDDLGenerator.java
index 1c37980b..db607d41 100644
--- a/empire-db/src/main/java/org/apache/empire/db/DBDDLGenerator.java
+++ b/empire-db/src/main/java/org/apache/empire/db/DBDDLGenerator.java
@@ -232,7 +232,7 @@ public abstract class DBDDLGenerator<T extends DBMSHandler>
         // Default Value
         if (isDDLColumnDefaults() && !c.isAutoGenerated() && c.getDefaultValue()!=null)
         {   sql.append(" DEFAULT ");
-            sql.append(dbms.getValueString(c.getDefaultValue(), c.getDataType()));
+            sql.appendValue(c.getDataType(), c.getDefaultValue());
         }
         // Nullable
         if (c.isRequired() ||  c.isAutoGenerated())
diff --git a/empire-db/src/main/java/org/apache/empire/db/DBSQLBuilder.java b/empire-db/src/main/java/org/apache/empire/db/DBSQLBuilder.java
index 420be5df..5f7864ad 100644
--- a/empire-db/src/main/java/org/apache/empire/db/DBSQLBuilder.java
+++ b/empire-db/src/main/java/org/apache/empire/db/DBSQLBuilder.java
@@ -18,14 +18,21 @@
  */
 package org.apache.empire.db;
 
+import java.sql.Timestamp;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.Collection;
+import java.util.Date;
 
 import org.apache.empire.commons.ObjectUtils;
 import org.apache.empire.commons.OptionEntry;
+import org.apache.empire.commons.StringUtils;
 import org.apache.empire.data.DataType;
 import org.apache.empire.dbms.DBMSHandler;
 import org.apache.empire.dbms.DBSqlPhrase;
 import org.apache.empire.exceptions.InvalidArgumentException;
+import org.apache.empire.exceptions.NotSupportedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -36,13 +43,15 @@ import org.slf4j.LoggerFactory;
  */
 public abstract class DBSQLBuilder implements Appendable
 {
-    private static final Logger log = LoggerFactory.getLogger(DBSQLBuilder.class);
-    
-    private final DBMSHandler dbms;
-    
-    private final StringBuilder sql = new StringBuilder(64);
-    
-    private DBCmdParamList cmdParamList;
+    private static final Logger log            = LoggerFactory.getLogger(DBSQLBuilder.class);
+
+    protected static final char   TEXT_DELIMITER = '\'';
+
+    protected final DBMSHandler   dbms;
+
+    protected final StringBuilder sql            = new StringBuilder(64);
+
+    protected DBCmdParamList      cmdParamList;
 
     /**
      *  Don't use this directly
@@ -58,6 +67,15 @@ public abstract class DBSQLBuilder implements Appendable
         this.cmdParamList = cmdParamList;
     }
     
+    /**
+     * returns the SQL as a String 
+     */
+    @Override
+    public String toString()
+    {
+        return sql.toString();
+    }
+    
     /*
      * getters
      */
@@ -104,6 +122,23 @@ public abstract class DBSQLBuilder implements Appendable
         sql.append(sqlLiteral, start, end);
         return this;
     }
+
+    @Override
+    public DBSQLBuilder append(char c)
+    {
+        sql.append(c);
+        return this;
+    }
+
+    public DBSQLBuilder append(long l) {
+        sql.append(l);
+        return this;
+    }
+
+    public DBSQLBuilder append(double d) {
+        sql.append(d);
+        return this;
+    }
     
     public DBSQLBuilder append(DBSqlPhrase phrase)
     {
@@ -113,66 +148,35 @@ public abstract class DBSQLBuilder implements Appendable
     
     public void append(DBCommandExpr subQueryCmd)
     {
-        // subQueryCmd.addSQL(this, DBExpr.CTX_DEFAULT);
+        // append select
         sql.append(subQueryCmd.getSelect());
-        
+        // check params
         DBCmdParams params = subQueryCmd.getParams();
         if (params.isEmpty())
             return;
-
+        // cmdParamList
+        if (cmdParamList==null)
+            throw new NotSupportedException(this, "append command with params");
+        // merge
         cmdParamList.mergeSubqueryParams(params);
-        
-        /*
-        // Check CmdParams
-        Object[] paramValues = subQueryCmd.getParamValues();
-        if (paramValues!=null && paramValues.length>0)
-        {   // Params Available
-            cmdParamList.mergeSubqueryParams(paramValues);
-        }
-        */
     }
 
-    @Override
-    public DBSQLBuilder append(char c)
+    /**
+     * Appends the SQL representation of a value
+     * @param dataType the DataType
+     * @param value an DBExpr object, array or a basis data type(e.g. int, String)
+     */
+    public void appendValue(DataType type, Object value)
     {
-        sql.append(c);
-        return this;
+        appendValue(type, value, DBExpr.CTX_DEFAULT, "+");
     }
     
-    public DBSQLBuilder append(boolean b) {
-        sql.append(b);
-        return this;
-    }
-
-    public DBSQLBuilder append(int i) {
-        sql.append(i);
-        return this;
-    }
-
-    public DBSQLBuilder append(long l) {
-        sql.append(l);
-        return this;
-    }
-
-    public DBSQLBuilder append(float f) {
-        sql.append(f);
-        return this;
-    }
-
-    public DBSQLBuilder append(double d) {
-        sql.append(d);
-        return this;
-    }
-
     /**
      * Appends the SQL representation of a value
-     * 
-     * @param StringBuilder but the SQL builder
      * @param dataType the DataType
      * @param value an DBExpr object, array or a basis data type(e.g. int, String)
      * @param context the context of the DBColumnExpr object
      * @param arraySep the separator value
-     * @return the new SQL-Command
      */
     public void appendValue(DataType dataType, Object value, long context, String arraySep)
     {
@@ -211,7 +215,7 @@ public abstract class DBSQLBuilder implements Appendable
         } 
         else
         {   // Get Value Expression from dmbs
-            sql.append(dbms.getValueString(value, dataType));
+            appendSimpleValue(dataType, value);
         }
     }
     
@@ -260,12 +264,233 @@ public abstract class DBSQLBuilder implements Appendable
         }
     }
     
+    /*
+     * internal
+     */
+    
     /**
-     * returns the SQL as a String 
+     * Returns a sql string for a given value. 
+     * Text will be enclosed in single quotes and existing single quotes will be doubled.
+     * Empty strings are treated as null.
+     * The syntax of Date, Datetime and Boolean values are DBMS specific.
+     * 
+     * @param value the value which is inserted to the new String
+     * @param type the sql data type of the supplied value
+     * @return the sql string representing this value
      */
-    @Override
-    public String toString()
+    protected void appendSimpleValue(DataType type, Object value)
+    { 
+        if (value instanceof Enum<?>)
+        {   // convert enum
+            log.warn("Enum of type {} supplied for getValueString. Converting value...", value.getClass().getName());
+            value = ObjectUtils.getEnumValue((Enum<?>)value, type.isNumeric());
+        }
+        if (ObjectUtils.isEmpty(value))
+        {   // null
+            sql.append(DBSqlPhrase.SQL_NULL);
+            return;
+        }
+        // set string buffer
+        switch (type)
+        {
+            case DATE:
+                sql.append(getDateTimeString(value, DBSqlPhrase.SQL_DATE_TEMPLATE, DBSqlPhrase.SQL_DATE_PATTERN, DBSqlPhrase.SQL_CURRENT_DATE));
+                return;
+            case TIME:
+                sql.append(getDateTimeString(value, DBSqlPhrase.SQL_TIME_TEMPLATE, DBSqlPhrase.SQL_TIME_PATTERN, DBSqlPhrase.SQL_CURRENT_TIME));
+                return;
+            case DATETIME:
+                String text;
+                // Only date (without time) provided?
+                if (!DBDatabase.SYSDATE.equals(value) && !(value instanceof Date) && ObjectUtils.lengthOf(value)<=10)
+                    text = getDateTimeString(value, DBSqlPhrase.SQL_DATE_TEMPLATE, DBSqlPhrase.SQL_DATE_PATTERN, DBSqlPhrase.SQL_CURRENT_TIMESTAMP);
+                else // Complete Date-Time Object with time
+                    text = getDateTimeString(value, DBSqlPhrase.SQL_DATETIME_TEMPLATE, DBSqlPhrase.SQL_DATETIME_PATTERN, DBSqlPhrase.SQL_CURRENT_TIMESTAMP);
+                sql.append(text);
+                return;
+            case TIMESTAMP:
+                sql.append(getDateTimeString(value, DBSqlPhrase.SQL_TIMESTAMP_TEMPLATE, DBSqlPhrase.SQL_TIMESTAMP_PATTERN, DBSqlPhrase.SQL_CURRENT_TIMESTAMP));
+                return;
+            case VARCHAR:
+            case CHAR:
+            case CLOB:
+            case UNIQUEID:
+                appendStringLiteral(type, value);
+                return;
+            case BOOL:
+                // Get Boolean value   
+                boolean boolVal = false;
+                if (value instanceof Boolean)
+                {   boolVal = ((Boolean) value).booleanValue();
+                } 
+                else
+                {   // Boolean from String
+                    boolVal = stringToBoolean(value.toString());
+                }
+                sql.append((boolVal) ? DBSqlPhrase.SQL_BOOLEAN_TRUE : DBSqlPhrase.SQL_BOOLEAN_FALSE);
+                return;
+            case INTEGER:
+            case DECIMAL:
+            case FLOAT:
+                sql.append(getNumberString(value, type));
+                return;
+            case BLOB:
+                throw new NotSupportedException(this, "appendSimpleValue(DataType.BLOB)"); 
+            case AUTOINC:
+            case UNKNOWN:
+                /* Allow expressions */
+                sql.append(value.toString());
+                return;
+            default:
+                log.warn("Unknown DataType {} for getValueString().", type);
+                sql.append(value.toString());
+        }
+    }
+
+    /**
+     * encodes a Date value for an SQL command string. 
+     * @param value
+     * @param sqlTemplate
+     * @param sqlPattern
+     * @param sqlCurrentDate
+     * @return
+     */
+    protected String getDateTimeString(Object value, DBSqlPhrase sqlTemplate, DBSqlPhrase sqlPattern, DBSqlPhrase sqlCurrentDate)
     {
-        return sql.toString();
+        // is it a sysdate expression
+        if (DBDatabase.SYSDATE.equals(value))
+            return dbms.getSQLPhrase(sqlCurrentDate);
+        // Format the date (ymd)
+        Timestamp ts; 
+        if ((value instanceof Timestamp)) 
+        {   // We have a timestamp
+            ts = (Timestamp)value;
+        }
+        else if ((value instanceof Date))
+        {   // Convert Date to Timestamp
+            ts = new Timestamp(((Date)value).getTime());
+        }
+        else if ((value instanceof LocalDate))
+        {   // Convert LocalDate to Timestamp
+            ts = java.sql.Timestamp.valueOf(((LocalDate)value).atStartOfDay());
+        }
+        else if ((value instanceof LocalDateTime))
+        {   // Convert LocalDateTime to Timestamp
+            ts = java.sql.Timestamp.valueOf((LocalDateTime)value);
+        }
+        else 
+        {   // "Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]"
+            String dtValue = value.toString().trim();
+            try
+            {   // parse timestamp
+                ts = Timestamp.valueOf(dtValue);
+            } catch (Throwable e) {
+                // Invalid date
+                log.error("Unable to parse date value "+dtValue, e);
+                throw new InvalidArgumentException("value", value);
+            }
+        }
+        // Convert to String
+        String pattern = dbms.getSQLPhrase(sqlPattern);
+        SimpleDateFormat sqlFormat = new SimpleDateFormat(dbms.getSQLPhrase(sqlPattern));
+        String datetime = sqlFormat.format(ts);
+        // Add micro / nanoseconds
+        int nanos = (ts.getNanos() % 1000000);
+        if (pattern.endsWith(".SSS") && nanos>0)
+        {   // Add nanoseconds
+            if (((nanos) % 100)>0)
+                datetime += String.format("%06d", nanos);
+            else
+                datetime += String.format("%04d",(nanos/100));
+        }
+        // Now Build String
+        String template = dbms.getSQLPhrase(sqlTemplate);
+        return StringUtils.replace(template, "{0}", datetime);
+    }
+
+    /**
+     * encodes Text values for an SQL command string.
+     * @param type date type (can only be TEXT, CHAR, CLOB and UNIQUEID)
+     * @param text the text to be encoded
+     * @return the encoded sql value
+     */
+    protected void appendStringLiteral(DataType type, Object value)
+    {   // text
+        if (value==null)
+        {   append(DBSqlPhrase.SQL_NULL);
+            return;
+        }
+        String text = value.toString();
+        sql.append(TEXT_DELIMITER);
+        if (DBDatabase.EMPTY_STRING.equals(text)==false)
+            escapeAndAppendLiteral(text);
+        sql.append(TEXT_DELIMITER);
+    }
+
+    /** 
+     * this helper function doubles up single quotes for SQL 
+     */
+    protected void escapeAndAppendLiteral(String value)
+    {
+        int pos = 0;
+        int delim;
+        // find delimiter
+        while ((delim = value.indexOf(TEXT_DELIMITER, pos))>=0)
+        {   // append
+            if (delim>pos)
+                sql.append(value.substring(pos, delim));
+            // double up
+            sql.append("''");
+            // next
+            pos = delim + 1;
+        }
+        if (pos==0)
+            sql.append(value); // add entire string
+        else if (pos < value.length())
+            sql.append(value.substring(pos)); // add the rest
     }
+    
+    /**
+     * encodes a numeric value for an SQL command string. 
+     * @param value the numeric value
+     * @param type the number data type
+     * @return the string representation of the number
+     */
+    protected String getNumberString(Object value, DataType type)
+    {
+        // already a number
+        if (value instanceof Number)
+            return value.toString();
+        
+        // check if it is a number
+        String s = value.toString();
+        boolean integerOnly = (type==DataType.INTEGER);
+        for (int i=0; i<s.length(); i++)
+        {
+            char c = s.charAt(i);
+            if (c>='0' && c<='9')
+                continue; // OK
+            if (c=='-' || c=='+')
+                continue; // OK
+            if (c==' ' && i>0)
+                return s.substring(0,i);
+            // check 
+            if (integerOnly || (c!='.' && c!=','))
+                throw new NumberFormatException(s);
+        }
+        return s;
+    }
+
+    /**
+     * this function converts a string containing a boolean expression to a boolean. 
+     * @param value the string containing a boolean expression
+     * @return true if the string contains either "true", "y" or "1" or false otherwise
+     */
+    protected boolean stringToBoolean(final String value) 
+    {
+        return "1".equals(value) ||
+               "true".equalsIgnoreCase(value) ||
+               "y".equalsIgnoreCase(value);
+    }
+    
 }
diff --git a/empire-db/src/main/java/org/apache/empire/db/expr/column/DBValueExpr.java b/empire-db/src/main/java/org/apache/empire/db/expr/column/DBValueExpr.java
index 18f60eb0..db7f71de 100644
--- a/empire-db/src/main/java/org/apache/empire/db/expr/column/DBValueExpr.java
+++ b/empire-db/src/main/java/org/apache/empire/db/expr/column/DBValueExpr.java
@@ -27,10 +27,10 @@ import org.apache.empire.data.DataType;
 import org.apache.empire.db.DBColumn;
 import org.apache.empire.db.DBColumnExpr;
 import org.apache.empire.db.DBDatabase;
-import org.apache.empire.db.DBExpr;
 import org.apache.empire.db.DBSQLBuilder;
-import org.apache.empire.dbms.DBMSHandler;
 import org.apache.empire.xml.XMLUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.w3c.dom.Element;
 
 
@@ -45,6 +45,7 @@ import org.w3c.dom.Element;
 public class DBValueExpr extends DBColumnExpr
 {
     // *Deprecated* private static final long serialVersionUID = 1L;
+    private static final Logger log = LoggerFactory.getLogger(DBValueExpr.class);
   
     public final DBDatabase   db;
     public final DataType     type;
@@ -234,21 +235,10 @@ public class DBValueExpr extends DBColumnExpr
     @Override
     public void addSQL(DBSQLBuilder sql, long context)
     {
-        if (value instanceof DBExpr)
-        {   // its an expression
-            ((DBExpr)value).addSQL(sql, context);
-        }
+        if ((context & CTX_VALUE)!=0)
+            sql.appendValue(getDataType(), value);
         else
-        {   // unpack
-            DataType dataType = getDataType();
-            Object dataValue = value;
-            if (dataValue instanceof Enum<?>)
-                dataValue = ObjectUtils.getEnumValue((Enum<?>)dataValue, dataType.isNumeric());
-            // convert value to sql literal
-            DBMSHandler dbms = db.getDbms();
-            String text = (dbms!=null) ? dbms.getValueString(dataValue, dataType) : ObjectUtils.getString(dataValue); 
-            sql.append(text);
-        }
+            log.warn("Cannot add SQL for DBValueExpr using context {}", context);
     }
 
     /**
diff --git a/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandler.java b/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandler.java
index 84f12491..3d6583c5 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandler.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandler.java
@@ -116,18 +116,6 @@ public interface DBMSHandler
      */
     String getConvertPhrase(DataType destType, DataType srcType, Object format);
 
-    /**
-     * Creates a sql string for a given value. 
-     * Text will be enclosed in single quotes and existing single quotes will be doubled.
-     * Empty strings are treated as null.
-     * Syntax of Date, Datetime and Boolean values are vendor specific.
-     * 
-     * @param value the value which is inserted to the new String
-     * @param type the sql data type of the supplied value
-     * @return the sql string representing this value
-     */
-    String getValueString(Object value, DataType type);
-
     /**
      * Returns a DMBS-Timestamp that is used for record updates.
      * @param conn the connection that might be used 
diff --git a/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandlerBase.java b/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandlerBase.java
index 0067880e..2f25cdb5 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandlerBase.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandlerBase.java
@@ -24,9 +24,6 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.sql.Timestamp;
-import java.text.SimpleDateFormat;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
@@ -71,8 +68,6 @@ import org.slf4j.LoggerFactory;
 public abstract class DBMSHandlerBase implements DBMSHandler
 {
     private static final Logger log = LoggerFactory.getLogger(DBMSHandler.class);
-    
-    protected static final char     TEXT_DELIMITER       = '\'';
       
     // Illegal name chars and reserved SQL keywords
     protected static final char[]   ILLEGAL_NAME_CHARS   = new char[] { '@', '?', '>', '=', '<', ';', ':', 
@@ -529,75 +524,6 @@ public abstract class DBMSHandlerBase implements DBMSHandler
             return rset.getObject(columnIndex);
         }
     }
-   
-    /**
-     * Returns a sql string for a given value. 
-     * Text will be enclosed in single quotes and existing single quotes will be doubled.
-     * Empty strings are treated as null.
-     * The syntax of Date, Datetime and Boolean values are DBMS specific.
-     * 
-     * @param value the value which is inserted to the new String
-     * @param type the sql data type of the supplied value
-     * @return the sql string representing this value
-     */
-    @Override
-    public String getValueString(Object value, DataType type)
-    { 
-        if (value instanceof Enum<?>)
-        {   // convert enum
-            log.warn("Enum of type {} supplied for getValueString. Converting value...", value.getClass().getName());
-            value = ObjectUtils.getEnumValue((Enum<?>)value, type.isNumeric());
-        }
-        if (ObjectUtils.isEmpty(value))
-        {   // null
-            return getSQLPhrase(DBSqlPhrase.SQL_NULL);
-        }
-        // set string buffer
-        switch (type)
-        {
-            case DATE:
-                return getSQLDateTimeString(value, DBSqlPhrase.SQL_DATE_TEMPLATE, DBSqlPhrase.SQL_DATE_PATTERN, DBSqlPhrase.SQL_CURRENT_DATE);
-            case TIME:
-                return getSQLDateTimeString(value, DBSqlPhrase.SQL_TIME_TEMPLATE, DBSqlPhrase.SQL_TIME_PATTERN, DBSqlPhrase.SQL_CURRENT_TIME);
-            case DATETIME:
-                // Only date (without time) provided?
-                if (!DBDatabase.SYSDATE.equals(value) && !(value instanceof Date) && ObjectUtils.lengthOf(value)<=10)
-                    return getSQLDateTimeString(value, DBSqlPhrase.SQL_DATE_TEMPLATE, DBSqlPhrase.SQL_DATE_PATTERN, DBSqlPhrase.SQL_CURRENT_TIMESTAMP);
-                // Complete Date-Time Object with time 
-                return getSQLDateTimeString(value, DBSqlPhrase.SQL_DATETIME_TEMPLATE, DBSqlPhrase.SQL_DATETIME_PATTERN, DBSqlPhrase.SQL_CURRENT_TIMESTAMP);
-            case TIMESTAMP:
-                return getSQLDateTimeString(value, DBSqlPhrase.SQL_TIMESTAMP_TEMPLATE, DBSqlPhrase.SQL_TIMESTAMP_PATTERN, DBSqlPhrase.SQL_CURRENT_TIMESTAMP);
-            case VARCHAR:
-            case CHAR:
-            case CLOB:
-            case UNIQUEID:
-                return getSQLStringLiteral(type, value);
-            case BOOL:
-                // Get Boolean value   
-                boolean boolVal = false;
-                if (value instanceof Boolean)
-                {   boolVal = ((Boolean) value).booleanValue();
-                } 
-                else
-                { // Boolean from String
-                    boolVal = stringToBoolean(value.toString());
-                }
-                return getSQLPhrase((boolVal) ? DBSqlPhrase.SQL_BOOLEAN_TRUE : DBSqlPhrase.SQL_BOOLEAN_FALSE);
-            case INTEGER:
-            case DECIMAL:
-            case FLOAT:
-                return getSQLNumberString(value, type);
-            case BLOB:
-                throw new NotSupportedException(this, "getValueString(?, DataType.BLOB)"); 
-            case AUTOINC:
-            case UNKNOWN:
-                /* Allow expressions */
-                return value.toString();
-            default:
-                log.warn("Unknown DataType {} for getValueString().", type);
-                return value.toString();
-        }
-    }
     
     /**
      * Executes the select, update or delete SQL-Command with a Statement object.
@@ -981,151 +907,5 @@ public abstract class DBMSHandlerBase implements DBMSHandler
                 log.trace("Statement param {} set to '{}'", paramIndex, value);
         }
     }
-    
-    /**
-     * encodes a numeric value for an SQL command string. 
-     * @param value the numeric value
-     * @param type the number data type
-     * @return the string reprentation of the number
-     */
-    protected String getSQLNumberString(Object value, DataType type)
-    {
-        // already a number
-        if (value instanceof Number)
-            return value.toString();
-        
-        // check if it is a number
-        String s = value.toString();
-        boolean integerOnly = (type==DataType.INTEGER);
-        for (int i=0; i<s.length(); i++)
-        {
-            char c = s.charAt(i);
-            if (c>='0' && c<='9')
-                continue; // OK
-            if (c=='-' || c=='+')
-                continue; // OK
-            if (c==' ' && i>0)
-                return s.substring(0,i);
-            // check 
-            if (integerOnly || (c!='.' && c!=','))
-                throw new NumberFormatException(s);
-        }
-        return s;
-    }
-
-    /**
-     * encodes a Date value for an SQL command string. 
-     * @param value
-     * @param sqlTemplate
-     * @param sqlPattern
-     * @param sqlCurrentDate
-     * @return
-     */
-    protected String getSQLDateTimeString(Object value, DBSqlPhrase sqlTemplate, DBSqlPhrase sqlPattern, DBSqlPhrase sqlCurrentDate)
-    {
-        // is it a sysdate expression
-        if (DBDatabase.SYSDATE.equals(value))
-            return getSQLPhrase(sqlCurrentDate);
-        // Format the date (ymd)
-        Timestamp ts; 
-        if ((value instanceof Timestamp)) 
-        {   // We have a timestamp
-            ts = (Timestamp)value;
-        }
-        else if ((value instanceof Date))
-        {   // Convert Date to Timestamp
-            ts = new Timestamp(((Date)value).getTime());
-        }
-        else if ((value instanceof LocalDate))
-        {   // Convert LocalDate to Timestamp
-            ts = java.sql.Timestamp.valueOf(((LocalDate)value).atStartOfDay());
-        }
-        else if ((value instanceof LocalDateTime))
-        {   // Convert LocalDateTime to Timestamp
-            ts = java.sql.Timestamp.valueOf((LocalDateTime)value);
-        }
-        else 
-        {   // "Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]"
-            String dtValue = value.toString().trim();
-            try
-            {   // parse timestamp
-                ts = Timestamp.valueOf(dtValue);
-            } catch (Throwable e) {
-                // Invalid date
-                log.error("Unable to parse date value "+dtValue, e);
-                throw new InvalidArgumentException("value", value);
-            }
-        }
-        // Convert to String
-        String pattern = getSQLPhrase(sqlPattern);
-        SimpleDateFormat sqlFormat = new SimpleDateFormat(getSQLPhrase(sqlPattern));
-        String datetime = sqlFormat.format(ts);
-        // Add micro / nanoseconds
-        int nanos = (ts.getNanos() % 1000000);
-        if (pattern.endsWith(".SSS") && nanos>0)
-        {   // Add nanoseconds
-            if (((nanos) % 100)>0)
-                datetime += String.format("%06d", nanos);
-            else
-                datetime += String.format("%04d",(nanos/100));
-        }
-        // Now Build String
-        String template = getSQLPhrase(sqlTemplate);
-        return StringUtils.replace(template, "{0}", datetime);
-    }
-
-    /**
-     * encodes Text values for an SQL command string.
-     * @param type date type (can only be TEXT, CHAR, CLOB and UNIQUEID)
-     * @param text the text to be encoded
-     * @return the encoded sql value
-     */
-    protected String getSQLStringLiteral(DataType type, Object value)
-    {   // text
-        if (value==null)
-            return getSQLPhrase(DBSqlPhrase.SQL_NULL); 
-        String text = value.toString();
-        StringBuilder sql = new StringBuilder(text.length()+2);
-        sql.append(TEXT_DELIMITER);
-        if (DBDatabase.EMPTY_STRING.equals(text)==false)
-            appendSQLTextValue(sql, text);
-        sql.append(TEXT_DELIMITER);
-        return sql.toString();
-    }
-
-    /** 
-     * this helper function doubles up single quotes for SQL 
-     */
-    protected void appendSQLTextValue(StringBuilder sql, String value)
-    {
-        int pos = 0;
-        int delim;
-        // find delimiter
-        while ((delim = value.indexOf(TEXT_DELIMITER, pos))>=0)
-        {   // append
-            if (delim>pos)
-                sql.append(value.substring(pos, delim));
-            // double up
-            sql.append("''");
-            // next
-            pos = delim + 1;
-        }
-        if (pos==0)
-            sql.append(value); // add entire string
-        else if (pos < value.length())
-            sql.append(value.substring(pos)); // add the rest
-    }
-
-    /**
-     * this function converts a string containing a boolean expression to a boolean. 
-     * @param value the string containing a boolean expression
-     * @return true if the string contains either "true", "y" or "1" or false otherwise
-     */
-    protected boolean stringToBoolean(final String value) 
-    {
-        return "1".equals(value) ||
-               "true".equalsIgnoreCase(value) ||
-               "y".equalsIgnoreCase(value);
-    }
 
 }
\ No newline at end of file
diff --git a/empire-db/src/main/java/org/apache/empire/dbms/derby/DerbyDDLGenerator.java b/empire-db/src/main/java/org/apache/empire/dbms/derby/DerbyDDLGenerator.java
index 352c37e8..5dd0cd4a 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/derby/DerbyDDLGenerator.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/derby/DerbyDDLGenerator.java
@@ -76,7 +76,7 @@ public class DerbyDDLGenerator extends DBDDLGenerator<DBMSHandlerDerby>
         // Default Value
         if (isDDLColumnDefaults() && !c.isAutoGenerated() && c.getDefaultValue()!=null)
         {   sql.append(" DEFAULT ");
-            sql.append(dbms.getValueString(c.getDefaultValue(), c.getDataType()));
+            sql.appendValue(c.getDataType(), c.getDefaultValue());
         }
         // Nullable
         if (c.isRequired() ||  c.isAutoGenerated())
diff --git a/empire-db/src/main/java/org/apache/empire/dbms/mysql/DBMSHandlerMySQL.java b/empire-db/src/main/java/org/apache/empire/dbms/mysql/DBMSHandlerMySQL.java
index eb07418d..ad1ddd03 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/mysql/DBMSHandlerMySQL.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/mysql/DBMSHandlerMySQL.java
@@ -124,6 +124,43 @@ public class DBMSHandlerMySQL extends DBMSHandlerBase
         }
     }
     
+    /**
+     * Provides a DBSQLBuilder implementation for MySQL
+     */
+    public static class DBSQLBuilderMySQL extends DBSQLBuilder 
+    {
+        public DBSQLBuilderMySQL(DBMSHandlerMySQL dbms)
+        {
+            super(dbms);
+        }
+        
+        @Override
+        protected void escapeAndAppendLiteral(String value)
+        {
+            if (value.indexOf('\'') >= 0 || value.indexOf('\\') >= 0)
+            {
+                int len = value.length();
+                for (int i = 0; i < len; i++)
+                {
+                    if (value.charAt(i) == '\'')
+                    {   // a routine to double up single quotes for SQL
+                        sql.append("''");
+                    }
+                    else if (value.charAt(i) == '\\')
+                    {   // a routine to double up backslashes for MySQL
+                        sql.append("\\\\");
+                    } 
+                    else
+                    {   // normal
+                        sql.append(value.charAt(i));
+                    }
+                }
+            } else {
+                sql.append(value);
+            }
+        }
+    }
+    
     // Properties
     private String databaseName = null;
     private String characterSet = "utf8";
@@ -904,6 +941,16 @@ public class DBMSHandlerMySQL extends DBMSHandlerBase
         return new DBCommandMySQL(autoPrepareStmt);
     }
 
+    /**
+     * Creates a new MySQL SQL-Builder.
+     * @return the new DBSQLBuilder object
+     */
+    @Override
+    public DBSQLBuilder createSQLBuilder()
+    {
+        return new DBSQLBuilderMySQL(this);
+    }
+
     @Override
     /**
      * Creates a combined command that supports limit() and skip()
@@ -1138,33 +1185,5 @@ public class DBMSHandlerMySQL extends DBMSHandlerBase
         ddlGenerator.getDDLScript(type, dbo, script); 
     }
 
-    /** 
-     * this helper function doubles up single quotes for SQL 
-     */
-    @Override
-    protected void appendSQLTextValue(StringBuilder buf, String value)
-    {
-        if (value.indexOf('\'') >= 0 || value.indexOf('\\') >= 0)
-        {
-        	int len = value.length();
-            for (int i = 0; i < len; i++)
-            {
-                if (value.charAt(i) == '\'')
-                { // a routine to double up single quotes for SQL
-                    buf.append("''");
-                }
-                else if (value.charAt(i) == '\\')
-                { // a routine to double up backslashes for MySQL
-                	buf.append("\\\\");
-                } else
-                {
-                	buf.append(value.charAt(i));
-                }
-            }
-        } else 
-        {
-            buf.append(value);
-        }
-    }
     
 }
diff --git a/empire-db/src/main/java/org/apache/empire/dbms/postgresql/PostgresDDLGenerator.java b/empire-db/src/main/java/org/apache/empire/dbms/postgresql/PostgresDDLGenerator.java
index 5a575318..a318b42c 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/postgresql/PostgresDDLGenerator.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/postgresql/PostgresDDLGenerator.java
@@ -181,7 +181,7 @@ public class PostgresDDLGenerator extends DBDDLGenerator<DBMSHandlerPostgreSQL>
         // Default Value
         if (isDDLColumnDefaults() && !c.isAutoGenerated() && c.getDefaultValue()!=null)
         {   sql.append(" DEFAULT ");
-            sql.append(dbms.getValueString(c.getDefaultValue(), c.getDataType()));
+            sql.appendValue(c.getDataType(), c.getDefaultValue());
         }
         // Nullable
         if (c.isRequired() ||  c.isAutoGenerated())
diff --git a/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/DBMSHandlerMSSQL.java b/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/DBMSHandlerMSSQL.java
index 6bd8f12d..70f7adab 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/DBMSHandlerMSSQL.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/DBMSHandlerMSSQL.java
@@ -59,8 +59,8 @@ public class DBMSHandlerMSSQL extends DBMSHandlerBase
     private static final Logger log = LoggerFactory.getLogger(DBMSHandlerMSSQL.class);
   
     /**
-     * Defines the Microsoft SQL-Server command type.
-     */ 
+     * Provides a DBCommand implementation for Microsoft SQL-Server
+     */
     public static class DBCommandMSSQL extends DBCommand
     {
         // *Deprecated* private static final long serialVersionUID = 1L;
@@ -103,6 +103,36 @@ public class DBMSHandlerMSSQL extends DBMSHandlerBase
         }
     }
     
+    /**
+     * Provides a DBSQLBuilder implementation for Microsoft SQL-Server
+     */
+    public static class DBSQLBuilderMSSQL extends DBSQLBuilder 
+    {
+        private final boolean useUnicodePrefix;
+        
+        public DBSQLBuilderMSSQL(DBMSHandlerMSSQL dbms)
+        {
+            super(dbms);
+            // init
+            this.useUnicodePrefix = dbms.useUnicodePrefix;
+        }
+        
+        @Override
+        protected void appendStringLiteral(DataType type, Object value)
+        {   // text
+            if (value==null)
+            {   append(DBSqlPhrase.SQL_NULL);
+                return;
+            }
+            String text = value.toString();
+            // for SQLSERVER utf8 support, see EMPIREDB-122
+            sql.append((useUnicodePrefix) ? "N'" : "'");
+            if (DBDatabase.EMPTY_STRING.equals(text)==false)
+                escapeAndAppendLiteral(text);
+            sql.append(TEXT_DELIMITER);
+        }
+    }
+    
     // Properties
     private String databaseName = null;
     private String objectOwner = "dbo";
@@ -275,8 +305,7 @@ public class DBMSHandlerMSSQL extends DBMSHandlerBase
 
     /**
      * Creates a new Microsoft SQL-Server command object.
-     * 
-     * @return the new DBCommandMSSQL object
+     * @return the new DBCommand object
      */
     @Override
     public DBCommand createCommand(boolean autoPrepareStmt)
@@ -284,6 +313,16 @@ public class DBMSHandlerMSSQL extends DBMSHandlerBase
         // create command object
         return new DBCommandMSSQL(autoPrepareStmt);
     }
+    
+    /**
+     * Creates a new Microsoft SQL-Server SQL-Builder.
+     * @return the new DBSQLBuilder object
+     */
+    @Override
+    public DBSQLBuilder createSQLBuilder()
+    {
+        return new DBSQLBuilderMSSQL(this);
+    }
 
     /**
      * Returns whether or not a particular feature is supported by this dbms
@@ -452,24 +491,6 @@ public class DBMSHandlerMSSQL extends DBMSHandlerBase
         // automatic identity management
         return null;
     }
-
-    /**
-     * @see DBMSHandler#getSQLTextString(DataType type, Object value)
-     */
-    @Override
-    protected String getSQLStringLiteral(DataType type, Object value)
-    {   // text
-        if (value==null)
-            return getSQLPhrase(DBSqlPhrase.SQL_NULL); 
-        String text = value.toString();
-        StringBuilder sql = new StringBuilder(text.length()+2);
-        // for SQLSERVER utf8 support, see EMPIREDB-122
-        sql.append((useUnicodePrefix) ? "N'" : "'");
-        if (DBDatabase.EMPTY_STRING.equals(text)==false)
-            appendSQLTextValue(sql, text);
-        sql.append(TEXT_DELIMITER);
-        return sql.toString();
-    }
     
     /**
      * Overridden. Returns a timestamp that is used for record updates created by the database server.