Home » apache-openjpa-1.1.0-source » org.apache.openjpa.jdbc » sql » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    * http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.    
   18    */
   19   package org.apache.openjpa.jdbc.sql;
   20   
   21   import java.io.BufferedReader;
   22   import java.io.ByteArrayInputStream;
   23   import java.io.CharArrayReader;
   24   import java.io.IOException;
   25   import java.io.InputStream;
   26   import java.io.InputStreamReader;
   27   import java.io.OutputStream;
   28   import java.io.Reader;
   29   import java.io.StringReader;
   30   import java.io.Writer;
   31   import java.lang.reflect.InvocationTargetException;
   32   import java.lang.reflect.Method;
   33   import java.math.BigDecimal;
   34   import java.math.BigInteger;
   35   import java.sql.Array;
   36   import java.sql.Blob;
   37   import java.sql.Clob;
   38   import java.sql.Connection;
   39   import java.sql.DatabaseMetaData;
   40   import java.sql.PreparedStatement;
   41   import java.sql.Ref;
   42   import java.sql.ResultSet;
   43   import java.sql.SQLException;
   44   import java.sql.SQLWarning;
   45   import java.sql.Statement;
   46   import java.sql.Time;
   47   import java.sql.Timestamp;
   48   import java.sql.Types;
   49   import java.text.MessageFormat;
   50   import java.util.ArrayList;
   51   import java.util.Arrays;
   52   import java.util.Calendar;
   53   import java.util.Collection;
   54   import java.util.Collections;
   55   import java.util.Date;
   56   import java.util.HashSet;
   57   import java.util.Iterator;
   58   import java.util.LinkedHashSet;
   59   import java.util.List;
   60   import java.util.Locale;
   61   import java.util.Map;
   62   import java.util.Set;
   63   
   64   import javax.sql.DataSource;
   65   
   66   import org.apache.commons.lang.StringUtils;
   67   import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
   68   import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
   69   import org.apache.openjpa.jdbc.kernel.JDBCStore;
   70   import org.apache.openjpa.jdbc.kernel.exps.ExpContext;
   71   import org.apache.openjpa.jdbc.kernel.exps.ExpState;
   72   import org.apache.openjpa.jdbc.kernel.exps.FilterValue;
   73   import org.apache.openjpa.jdbc.kernel.exps.Val;
   74   import org.apache.openjpa.jdbc.meta.ClassMapping;
   75   import org.apache.openjpa.jdbc.meta.FieldMapping;
   76   import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
   77   import org.apache.openjpa.jdbc.schema.Column;
   78   import org.apache.openjpa.jdbc.schema.DataSourceFactory;
   79   import org.apache.openjpa.jdbc.schema.ForeignKey;
   80   import org.apache.openjpa.jdbc.schema.Index;
   81   import org.apache.openjpa.jdbc.schema.NameSet;
   82   import org.apache.openjpa.jdbc.schema.PrimaryKey;
   83   import org.apache.openjpa.jdbc.schema.Schema;
   84   import org.apache.openjpa.jdbc.schema.SchemaGroup;
   85   import org.apache.openjpa.jdbc.schema.Sequence;
   86   import org.apache.openjpa.jdbc.schema.Table;
   87   import org.apache.openjpa.jdbc.schema.Unique;
   88   import org.apache.openjpa.kernel.Filters;
   89   import org.apache.openjpa.kernel.OpenJPAStateManager;
   90   import org.apache.openjpa.kernel.exps.Path;
   91   import org.apache.openjpa.lib.conf.Configurable;
   92   import org.apache.openjpa.lib.conf.Configuration;
   93   import org.apache.openjpa.lib.jdbc.ConnectionDecorator;
   94   import org.apache.openjpa.lib.jdbc.LoggingConnectionDecorator;
   95   import org.apache.openjpa.lib.log.Log;
   96   import org.apache.openjpa.lib.util.Localizer;
   97   import org.apache.openjpa.lib.util.Localizer.Message;
   98   import org.apache.openjpa.meta.FieldMetaData;
   99   import org.apache.openjpa.meta.JavaTypes;
  100   import org.apache.openjpa.meta.ValueStrategies;
  101   import org.apache.openjpa.util.GeneralException;
  102   import org.apache.openjpa.util.InternalException;
  103   import org.apache.openjpa.util.InvalidStateException;
  104   import org.apache.openjpa.util.OpenJPAException;
  105   import org.apache.openjpa.util.ReferentialIntegrityException;
  106   import org.apache.openjpa.util.Serialization;
  107   import org.apache.openjpa.util.StoreException;
  108   import org.apache.openjpa.util.UnsupportedException;
  109   import org.apache.openjpa.util.UserException;
  110   
  111   import serp.util.Numbers;
  112   import serp.util.Strings;
  113   
  114   /**
  115    * Class which allows the creation of SQL dynamically, in a
  116    * database agnostic fashion. Subclass for the nuances of different data stores.
  117    */
  118   public class DBDictionary
  119       implements Configurable, ConnectionDecorator, JoinSyntaxes,
  120       LoggingConnectionDecorator.SQLWarningHandler {
  121   
  122       public static final String VENDOR_OTHER = "other";
  123       public static final String VENDOR_DATADIRECT = "datadirect";
  124   
  125       public static final String SCHEMA_CASE_UPPER = "upper";
  126       public static final String SCHEMA_CASE_LOWER = "lower";
  127       public static final String SCHEMA_CASE_PRESERVE = "preserve";
  128   
  129       public static final String CONS_NAME_BEFORE = "before";
  130       public static final String CONS_NAME_MID = "mid";
  131       public static final String CONS_NAME_AFTER = "after";
  132       
  133       public int blobBufferSize = 50000;
  134       public int clobBufferSize = 50000;
  135   
  136       protected static final int RANGE_POST_SELECT = 0;
  137       protected static final int RANGE_PRE_DISTINCT = 1;
  138       protected static final int RANGE_POST_DISTINCT = 2;
  139       protected static final int RANGE_POST_LOCK = 3;
  140   
  141       protected static final int NANO = 1;
  142       protected static final int MICRO = NANO * 1000;
  143       protected static final int MILLI = MICRO * 1000;
  144       protected static final int CENTI = MILLI * 10;
  145       protected static final int DECI = MILLI * 100;
  146       protected static final int SEC = MILLI * 1000;
  147   
  148       protected static final int NAME_ANY = 0;
  149       protected static final int NAME_TABLE = 1;
  150       protected static final int NAME_SEQUENCE = 2;
  151       
  152       protected static final int UNLIMITED = -1;
  153       protected static final int NO_BATCH = 0;
  154   
  155       private static final String ZERO_DATE_STR =
  156           "'" + new java.sql.Date(0) + "'";
  157       private static final String ZERO_TIME_STR = "'" + new Time(0) + "'";
  158       private static final String ZERO_TIMESTAMP_STR =
  159           "'" + new Timestamp(0) + "'";
  160   
  161       public static final List EMPTY_STRING_LIST = Arrays.asList(new String[]{});
  162       public static final List[] SQL_STATE_CODES = 
  163       	{EMPTY_STRING_LIST,                     // 0: Default
  164       	 Arrays.asList(new String[]{"41000"}),  // 1: LOCK
  165       	 EMPTY_STRING_LIST,                     // 2: OBJECT_NOT_FOUND
  166       	 EMPTY_STRING_LIST,                     // 3: OPTIMISTIC
  167       	 Arrays.asList(new String[]{"23000"}),  // 4: REFERENTIAL_INTEGRITY
  168       	 EMPTY_STRING_LIST                      // 5: OBJECT_EXISTS
  169       	}; 
  170                                                 
  171       private static final Localizer _loc = Localizer.forPackage
  172           (DBDictionary.class);
  173   
  174       // schema data
  175       public String platform = "Generic";
  176       public String driverVendor = null;
  177       public String catalogSeparator = ".";
  178       public boolean createPrimaryKeys = true;
  179       public String constraintNameMode = CONS_NAME_BEFORE;
  180       public int maxTableNameLength = 128;
  181       public int maxColumnNameLength = 128;
  182       public int maxConstraintNameLength = 128;
  183       public int maxIndexNameLength = 128;
  184       public int maxIndexesPerTable = Integer.MAX_VALUE;
  185       public boolean supportsForeignKeys = true;
  186       public boolean supportsTimestampNanos = true;
  187       public boolean supportsUniqueConstraints = true;
  188       public boolean supportsDeferredConstraints = true;
  189       public boolean supportsRestrictDeleteAction = true;
  190       public boolean supportsCascadeDeleteAction = true;
  191       public boolean supportsNullDeleteAction = true;
  192       public boolean supportsDefaultDeleteAction = true;
  193       public boolean supportsRestrictUpdateAction = true;
  194       public boolean supportsCascadeUpdateAction = true;
  195       public boolean supportsNullUpdateAction = true;
  196       public boolean supportsDefaultUpdateAction = true;
  197       public boolean supportsAlterTableWithAddColumn = true;
  198       public boolean supportsAlterTableWithDropColumn = true;
  199       public boolean supportsComments = false;
  200       public String reservedWords = null;
  201       public String systemSchemas = null;
  202       public String systemTables = null;
  203       public String selectWords = null;
  204       public String fixedSizeTypeNames = null;
  205       public String schemaCase = SCHEMA_CASE_UPPER;
  206   
  207       // sql
  208       public String validationSQL = null;
  209       public String closePoolSQL = null;
  210       public String initializationSQL = null;
  211       public int joinSyntax = SYNTAX_SQL92;
  212       public String outerJoinClause = "LEFT OUTER JOIN";
  213       public String innerJoinClause = "INNER JOIN";
  214       public String crossJoinClause = "CROSS JOIN";
  215       public boolean requiresConditionForCrossJoin = false;
  216       public String forUpdateClause = "FOR UPDATE";
  217       public String tableForUpdateClause = null;
  218       public String distinctCountColumnSeparator = null;
  219       public boolean supportsSelectForUpdate = true;
  220       public boolean supportsLockingWithDistinctClause = true;
  221       public boolean supportsLockingWithMultipleTables = true;
  222       public boolean supportsLockingWithOrderClause = true;
  223       public boolean supportsLockingWithOuterJoin = true;
  224       public boolean supportsLockingWithInnerJoin = true;
  225       public boolean supportsLockingWithSelectRange = true;
  226       public boolean supportsQueryTimeout = true;
  227       public boolean simulateLocking = false;
  228       public boolean supportsSubselect = true;
  229       public boolean supportsCorrelatedSubselect = true;
  230       public boolean supportsHaving = true;
  231       public boolean supportsSelectStartIndex = false;
  232       public boolean supportsSelectEndIndex = false;
  233       public int rangePosition = RANGE_POST_SELECT;
  234       public boolean requiresAliasForSubselect = false;
  235       public boolean requiresTargetForDelete = false;
  236       public boolean allowsAliasInBulkClause = true;
  237       public boolean supportsMultipleNontransactionalResultSets = true;
  238       public String searchStringEscape = "\\";
  239       public boolean requiresCastForMathFunctions = false;
  240       public boolean requiresCastForComparisons = false;
  241       public boolean supportsModOperator = false;
  242       public boolean supportsXMLColumn = false;
  243   
  244       // functions
  245       public String castFunction = "CAST({0} AS {1})";
  246       public String toLowerCaseFunction = "LOWER({0})";
  247       public String toUpperCaseFunction = "UPPER({0})";
  248       public String stringLengthFunction = "CHAR_LENGTH({0})";
  249       public String bitLengthFunction = "(OCTET_LENGTH({0}) * 8)";
  250       public String trimLeadingFunction = "TRIM(LEADING {1} FROM {0})";
  251       public String trimTrailingFunction = "TRIM(TRAILING {1} FROM {0})";
  252       public String trimBothFunction = "TRIM(BOTH {1} FROM {0})";
  253       public String concatenateFunction = "({0}||{1})";
  254       public String concatenateDelimiter = "'OPENJPATOKEN'";
  255       public String substringFunctionName = "SUBSTRING";
  256       public String currentDateFunction = "CURRENT_DATE";
  257       public String currentTimeFunction = "CURRENT_TIME";
  258       public String currentTimestampFunction = "CURRENT_TIMESTAMP";
  259       public String dropTableSQL = "DROP TABLE {0}";
  260   
  261       // types
  262       public boolean storageLimitationsFatal = false;
  263       public boolean storeLargeNumbersAsStrings = false;
  264       public boolean storeCharsAsNumbers = true;
  265       public boolean useGetBytesForBlobs = false;
  266       public boolean useSetBytesForBlobs = false;
  267       public boolean useGetObjectForBlobs = false;
  268       public boolean useGetStringForClobs = false;
  269       public boolean useSetStringForClobs = false;
  270       public int maxEmbeddedBlobSize = -1;
  271       public int maxEmbeddedClobSize = -1;
  272       public int inClauseLimit = -1;
  273       public int datePrecision = MILLI;
  274       public int characterColumnSize = 255;
  275       public String arrayTypeName = "ARRAY";
  276       public String bigintTypeName = "BIGINT";
  277       public String binaryTypeName = "BINARY";
  278       public String bitTypeName = "BIT";
  279       public String blobTypeName = "BLOB";
  280       public String booleanTypeName = "BOOLEAN";
  281       public String charTypeName = "CHAR";
  282       public String clobTypeName = "CLOB";
  283       public String dateTypeName = "DATE";
  284       public String decimalTypeName = "DECIMAL";
  285       public String distinctTypeName = "DISTINCT";
  286       public String doubleTypeName = "DOUBLE";
  287       public String floatTypeName = "FLOAT";
  288       public String integerTypeName = "INTEGER";
  289       public String javaObjectTypeName = "JAVA_OBJECT";
  290       public String longVarbinaryTypeName = "LONGVARBINARY";
  291       public String longVarcharTypeName = "LONGVARCHAR";
  292       public String nullTypeName = "NULL";
  293       public String numericTypeName = "NUMERIC";
  294       public String otherTypeName = "OTHER";
  295       public String realTypeName = "REAL";
  296       public String refTypeName = "REF";
  297       public String smallintTypeName = "SMALLINT";
  298       public String structTypeName = "STRUCT";
  299       public String timeTypeName = "TIME";
  300       public String timestampTypeName = "TIMESTAMP";
  301       public String tinyintTypeName = "TINYINT";
  302       public String varbinaryTypeName = "VARBINARY";
  303       public String varcharTypeName = "VARCHAR";
  304       public String xmlTypeName = "XML";
  305       public String getStringVal = "";
  306   
  307       // schema metadata
  308       public boolean useSchemaName = true;
  309       public String tableTypes = "TABLE";
  310       public boolean supportsSchemaForGetTables = true;
  311       public boolean supportsSchemaForGetColumns = true;
  312       public boolean supportsNullTableForGetColumns = true;
  313       public boolean supportsNullTableForGetPrimaryKeys = false;
  314       public boolean supportsNullTableForGetIndexInfo = false;
  315       public boolean supportsNullTableForGetImportedKeys = false;
  316       public boolean useGetBestRowIdentifierForPrimaryKeys = false;
  317       public boolean requiresAutoCommitForMetaData = false;
  318   
  319       // auto-increment
  320       public int maxAutoAssignNameLength = 31;
  321       public String autoAssignClause = null;
  322       public String autoAssignTypeName = null;
  323       public boolean supportsAutoAssign = false;
  324       public String lastGeneratedKeyQuery = null;
  325       public String nextSequenceQuery = null;
  326       public String sequenceSQL = null;
  327       public String sequenceSchemaSQL = null;
  328       public String sequenceNameSQL = null;
  329   
  330       protected JDBCConfiguration conf = null;
  331       protected Log log = null;
  332       protected boolean connected = false;
  333       protected final Set reservedWordSet = new HashSet();
  334       protected final Set systemSchemaSet = new HashSet();
  335       protected final Set systemTableSet = new HashSet();
  336       protected final Set fixedSizeTypeNameSet = new HashSet();
  337       protected final Set typeModifierSet = new HashSet();
  338   
  339       /**
  340        * If a native query begins with any of the values found here then it will
  341        * be treated as a select statement.  
  342        */
  343       protected final Set selectWordSet = new HashSet();
  344   
  345       // when we store values that lose precion, track the types so that the
  346       // first time it happens we can warn the user
  347       private Set _precisionWarnedTypes = null;
  348   
  349       // cache lob methods
  350       private Method _setBytes = null;
  351       private Method _setString = null;
  352       private Method _setCharStream = null;
  353   
  354       // batchLimit value:
  355       // -1 = unlimited
  356       // 0  = no batch
  357       // any positive number = batch limit
  358       public int batchLimit = NO_BATCH;
  359       
  360       public DBDictionary() {
  361           fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{
  362               "BIGINT", "BIT", "BLOB", "CLOB", "DATE", "DECIMAL", "DISTINCT",
  363               "DOUBLE", "FLOAT", "INTEGER", "JAVA_OBJECT", "NULL", "NUMERIC",
  364               "OTHER", "REAL", "REF", "SMALLINT", "STRUCT", "TIME", "TIMESTAMP",
  365               "TINYINT",
  366           }));
  367           
  368           selectWordSet.add("SELECT");
  369       }
  370   
  371       /**
  372        * This method is called when the dictionary first sees any connection.
  373        * It is used to initialize dictionary metadata if needed. If you
  374        * override this method, be sure to call
  375        * <code>super.connectedConfiguration</code>.
  376        */
  377       public void connectedConfiguration(Connection conn)
  378           throws SQLException {
  379           if (!connected) {
  380               try {
  381                   if (log.isTraceEnabled())
  382                       log.trace(DBDictionaryFactory.toString
  383                           (conn.getMetaData()));
  384               } catch (Exception e) {
  385                   log.trace(e.toString(), e);
  386               }
  387           }
  388           connected = true;
  389       }
  390   
  391       //////////////////////
  392       // ResultSet wrappers
  393       //////////////////////
  394   
  395       /**
  396        * Convert the specified column of the SQL ResultSet to the proper
  397        * java type.
  398        */
  399       public Array getArray(ResultSet rs, int column)
  400           throws SQLException {
  401           return rs.getArray(column);
  402       }
  403   
  404       /**
  405        * Convert the specified column of the SQL ResultSet to the proper
  406        * java type.
  407        */
  408       public InputStream getAsciiStream(ResultSet rs, int column)
  409           throws SQLException {
  410           return rs.getAsciiStream(column);
  411       }
  412   
  413       /**
  414        * Convert the specified column of the SQL ResultSet to the proper
  415        * java type.
  416        */
  417       public BigDecimal getBigDecimal(ResultSet rs, int column)
  418           throws SQLException {
  419           if (storeLargeNumbersAsStrings) {
  420               String str = getString(rs, column);
  421               return (str == null) ? null : new BigDecimal(str);
  422           }
  423           return rs.getBigDecimal(column);
  424       }
  425   
  426       /**
  427        * Returns the specified column value as an unknown numeric type;
  428        * we try from the most generic to the least generic.
  429        */
  430       public Number getNumber(ResultSet rs, int column)
  431           throws SQLException {
  432           // try from the most generic, and if errors occur, try
  433           // less generic types; this enables us to handle values
  434           // like Double.NaN without having to introspect on the
  435           // ResultSetMetaData (bug #1053). note that we handle
  436           // generic exceptions, since some drivers may throw
  437           // NumberFormatExceptions, whereas others may throw SQLExceptions
  438           try {
  439               return getBigDecimal(rs, column);
  440           } catch (Exception e1) {
  441               try {
  442                   return new Double(getDouble(rs, column));
  443               } catch (Exception e2) {
  444                   try {
  445                       return new Float(getFloat(rs, column));
  446                   } catch (Exception e3) {
  447                       try {
  448                           return Numbers.valueOf(getLong(rs, column));
  449                       } catch (Exception e4) {
  450                           try {
  451                               return Numbers.valueOf(getInt(rs, column));
  452                           } catch (Exception e5) {
  453                           }
  454                       }
  455                   }
  456               }
  457   
  458               if (e1 instanceof RuntimeException)
  459                   throw(RuntimeException) e1;
  460               if (e1 instanceof SQLException)
  461                   throw(SQLException) e1;
  462           }
  463   
  464           return null;
  465       }
  466   
  467       /**
  468        * Convert the specified column of the SQL ResultSet to the proper
  469        * java type.
  470        */
  471       public BigInteger getBigInteger(ResultSet rs, int column)
  472           throws SQLException {
  473           if (storeLargeNumbersAsStrings) {
  474               String str = getString(rs, column);
  475               return (str == null) ? null : new BigDecimal(str).toBigInteger();
  476           }
  477           BigDecimal bd = getBigDecimal(rs, column);
  478           return (bd == null) ? null : bd.toBigInteger();
  479       }
  480   
  481       /**
  482        * Convert the specified column of the SQL ResultSet to the proper
  483        * java type.
  484        */
  485       public InputStream getBinaryStream(ResultSet rs, int column)
  486           throws SQLException {
  487           return rs.getBinaryStream(column);
  488       }
  489   
  490       public InputStream getLOBStream(JDBCStore store, ResultSet rs,
  491           int column) throws SQLException {
  492           return rs.getBinaryStream(column);
  493       }
  494       
  495       /**
  496        * Convert the specified column of the SQL ResultSet to the proper
  497        * java type.
  498        */
  499       public Blob getBlob(ResultSet rs, int column)
  500           throws SQLException {
  501           return rs.getBlob(column);
  502       }
  503   
  504       /**
  505        * Convert the specified column of the SQL ResultSet to the proper
  506        * java type.
  507        */
  508       public Object getBlobObject(ResultSet rs, int column, JDBCStore store)
  509           throws SQLException {
  510           InputStream in = null;
  511           if (useGetBytesForBlobs || useGetObjectForBlobs) {
  512               byte[] bytes = getBytes(rs, column);
  513               if (bytes != null && bytes.length > 0)
  514                   in = new ByteArrayInputStream(bytes);
  515           } else {
  516               Blob blob = getBlob(rs, column);
  517               if (blob != null && blob.length() > 0)
  518                   in = blob.getBinaryStream();
  519           }
  520           if (in == null)
  521               return null;
  522   
  523           try {
  524               if (store == null)
  525                   return Serialization.deserialize(in, null);
  526               return Serialization.deserialize(in, store.getContext());
  527           } finally {
  528               try {
  529                   in.close();
  530               } catch (IOException ioe) {
  531               }
  532           }
  533       }
  534   
  535       /**
  536        * Convert the specified column of the SQL ResultSet to the proper
  537        * java type.
  538        */
  539       public boolean getBoolean(ResultSet rs, int column)
  540           throws SQLException {
  541           return rs.getBoolean(column);
  542       }
  543   
  544       /**
  545        * Convert the specified column of the SQL ResultSet to the proper
  546        * java type.
  547        */
  548       public byte getByte(ResultSet rs, int column)
  549           throws SQLException {
  550           return rs.getByte(column);
  551       }
  552   
  553       /**
  554        * Convert the specified column of the SQL ResultSet to the proper
  555        * java type.
  556        */
  557       public byte[] getBytes(ResultSet rs, int column)
  558           throws SQLException {
  559           if (useGetBytesForBlobs)
  560               return rs.getBytes(column);
  561           if (useGetObjectForBlobs)
  562               return (byte[]) rs.getObject(column);
  563   
  564           Blob blob = getBlob(rs, column);
  565           if (blob == null)
  566               return null;
  567           int length = (int) blob.length();
  568           if (length == 0)
  569               return null;
  570           return blob.getBytes(1, length);
  571       }
  572   
  573       /**
  574        * Convert the specified column of the SQL ResultSet to the proper
  575        * java type. Converts the date from a {@link Timestamp} by default.
  576        */
  577       public Calendar getCalendar(ResultSet rs, int column)
  578           throws SQLException {
  579           Date d = getDate(rs, column);
  580           if (d == null)
  581               return null;
  582   
  583           Calendar cal = Calendar.getInstance();
  584           cal.setTime(d);
  585           return cal;
  586       }
  587   
  588       /**
  589        * Convert the specified column of the SQL ResultSet to the proper
  590        * java type.
  591        */
  592       public char getChar(ResultSet rs, int column)
  593           throws SQLException {
  594           if (storeCharsAsNumbers)
  595               return (char) getInt(rs, column);
  596   
  597           String str = getString(rs, column);
  598           return (StringUtils.isEmpty(str)) ? 0 : str.charAt(0);
  599       }
  600   
  601       /**
  602        * Convert the specified column of the SQL ResultSet to the proper
  603        * java type.
  604        */
  605       public Reader getCharacterStream(ResultSet rs, int column)
  606           throws SQLException {
  607           return rs.getCharacterStream(column);
  608       }
  609   
  610       /**
  611        * Convert the specified column of the SQL ResultSet to the proper
  612        * java type.
  613        */
  614       public Clob getClob(ResultSet rs, int column)
  615           throws SQLException {
  616           return rs.getClob(column);
  617       }
  618   
  619       /**
  620        * Convert the specified column of the SQL ResultSet to the proper
  621        * java type.
  622        */
  623       public String getClobString(ResultSet rs, int column)
  624           throws SQLException {
  625           if (useGetStringForClobs)
  626               return rs.getString(column);
  627   
  628           Clob clob = getClob(rs, column);
  629           if (clob == null)
  630               return null;
  631           if (clob.length() == 0)
  632               return "";
  633   
  634           // unlikely that we'll have strings over Integer.MAX_VALUE chars
  635           return clob.getSubString(1, (int) clob.length());
  636       }
  637   
  638       /**
  639        * Convert the specified column of the SQL ResultSet to the proper
  640        * java type. Converts the date from a {@link Timestamp} by default.
  641        */
  642       public Date getDate(ResultSet rs, int column)
  643           throws SQLException {
  644           Timestamp tstamp = getTimestamp(rs, column, null);
  645           if (tstamp == null)
  646               return null;
  647   
  648           // get the fractional seconds component, rounding away anything beyond
  649           // milliseconds
  650           int fractional = (int) Math.round(tstamp.getNanos() / (double) MILLI);
  651   
  652           // get the millis component; some JDBC drivers round this to the
  653           // nearest second, while others do not
  654           long millis = (tstamp.getTime() / 1000L) * 1000L;
  655           return new Date(millis + fractional);
  656       }
  657   
  658       /**
  659        * Convert the specified column of the SQL ResultSet to the proper
  660        * java type.
  661        */
  662       public java.sql.Date getDate(ResultSet rs, int column, Calendar cal)
  663           throws SQLException {
  664           if (cal == null)
  665               return rs.getDate(column);
  666           return rs.getDate(column, cal);
  667       }
  668   
  669       /**
  670        * Convert the specified column of the SQL ResultSet to the proper
  671        * java type.
  672        */
  673       public double getDouble(ResultSet rs, int column)
  674           throws SQLException {
  675           return rs.getDouble(column);
  676       }
  677   
  678       /**
  679        * Convert the specified column of the SQL ResultSet to the proper
  680        * java type.
  681        */
  682       public float getFloat(ResultSet rs, int column)
  683           throws SQLException {
  684           return rs.getFloat(column);
  685       }
  686   
  687       /**
  688        * Convert the specified column of the SQL ResultSet to the proper
  689        * java type.
  690        */
  691       public int getInt(ResultSet rs, int column)
  692           throws SQLException {
  693           return rs.getInt(column);
  694       }
  695   
  696       /**
  697        * Convert the specified column of the SQL ResultSet to the proper
  698        * java type.
  699        */
  700       public Locale getLocale(ResultSet rs, int column)
  701           throws SQLException {
  702           String str = getString(rs, column);
  703           if (StringUtils.isEmpty(str))
  704               return null;
  705   
  706           String[] params = Strings.split(str, "_", 3);
  707           if (params.length < 3)
  708               return null;
  709           return new Locale(params[0], params[1], params[2]);
  710       }
  711   
  712       /**
  713        * Convert the specified column of the SQL ResultSet to the proper
  714        * java type.
  715        */
  716       public long getLong(ResultSet rs, int column)
  717           throws SQLException {
  718           return rs.getLong(column);
  719       }
  720   
  721       /**
  722        * Convert the specified column of the SQL ResultSet to the proper
  723        * java type.
  724        */
  725       public Object getObject(ResultSet rs, int column, Map map)
  726           throws SQLException {
  727           if (map == null)
  728               return rs.getObject(column);
  729           return rs.getObject(column, map);
  730       }
  731   
  732       /**
  733        * Convert the specified column of the SQL ResultSet to the proper
  734        * java type.
  735        */
  736       public Ref getRef(ResultSet rs, int column, Map map)
  737           throws SQLException {
  738           return rs.getRef(column);
  739       }
  740   
  741       /**
  742        * Convert the specified column of the SQL ResultSet to the proper
  743        * java type.
  744        */
  745       public short getShort(ResultSet rs, int column)
  746           throws SQLException {
  747           return rs.getShort(column);
  748       }
  749   
  750       /**
  751        * Convert the specified column of the SQL ResultSet to the proper
  752        * java type.
  753        */
  754       public String getString(ResultSet rs, int column)
  755           throws SQLException {
  756           return rs.getString(column);
  757       }
  758   
  759       /**
  760        * Convert the specified column of the SQL ResultSet to the proper
  761        * java type.
  762        */
  763       public Time getTime(ResultSet rs, int column, Calendar cal)
  764           throws SQLException {
  765           if (cal == null)
  766               return rs.getTime(column);
  767           return rs.getTime(column, cal);
  768       }
  769   
  770       /**
  771        * Convert the specified column of the SQL ResultSet to the proper
  772        * java type.
  773        */
  774       public Timestamp getTimestamp(ResultSet rs, int column, Calendar cal)
  775           throws SQLException {
  776           if (cal == null)
  777               return rs.getTimestamp(column);
  778           return rs.getTimestamp(column, cal);
  779       }
  780   
  781       //////////////////////////////
  782       // PreparedStatement wrappers
  783       //////////////////////////////
  784   
  785       /**
  786        * Set the given value as a parameter to the statement.
  787        */
  788       public void setArray(PreparedStatement stmnt, int idx, Array val,
  789           Column col)
  790           throws SQLException {
  791           stmnt.setArray(idx, val);
  792       }
  793   
  794       /**
  795        * Set the given value as a parameter to the statement.
  796        */
  797       public void setAsciiStream(PreparedStatement stmnt, int idx,
  798           InputStream val, int length, Column col)
  799           throws SQLException {
  800           stmnt.setAsciiStream(idx, val, length);
  801       }
  802   
  803       /**
  804        * Set the given value as a parameter to the statement.
  805        */
  806       public void setBigDecimal(PreparedStatement stmnt, int idx, BigDecimal val,
  807           Column col)
  808           throws SQLException {
  809           if ((col != null && col.isCompatible(Types.VARCHAR, null, 0, 0))
  810               || (col == null && storeLargeNumbersAsStrings))
  811               setString(stmnt, idx, val.toString(), col);
  812           else
  813               stmnt.setBigDecimal(idx, val);
  814       }
  815   
  816       /**
  817        * Set the given value as a parameter to the statement.
  818        */
  819       public void setBigInteger(PreparedStatement stmnt, int idx, BigInteger val,
  820           Column col)
  821           throws SQLException {
  822           if ((col != null && col.isCompatible(Types.VARCHAR, null, 0, 0))
  823               || (col == null && storeLargeNumbersAsStrings))
  824               setString(stmnt, idx, val.toString(), col);
  825           else
  826               setBigDecimal(stmnt, idx, new BigDecimal(val), col);
  827       }
  828   
  829       /**
  830        * Set the given value as a parameter to the statement.
  831        */
  832       public void setBinaryStream(PreparedStatement stmnt, int idx,
  833           InputStream val, int length, Column col)
  834           throws SQLException {
  835           stmnt.setBinaryStream(idx, val, length);
  836       }
  837   
  838       /**
  839        * Set the given value as a parameter to the statement.
  840        */
  841       public void setBlob(PreparedStatement stmnt, int idx, Blob val, Column col)
  842           throws SQLException {
  843           stmnt.setBlob(idx, val);
  844       }
  845   
  846       /**
  847        * Set the given value as a parameter to the statement. Uses the
  848        * {@link #serialize} method to serialize the value.
  849        */
  850       public void setBlobObject(PreparedStatement stmnt, int idx, Object val,
  851           Column col, JDBCStore store)
  852           throws SQLException {
  853           setBytes(stmnt, idx, serialize(val, store), col);
  854       }
  855   
  856       /**
  857        * Set the given value as a parameter to the statement.
  858        */
  859       public void setBoolean(PreparedStatement stmnt, int idx, boolean val,
  860           Column col)
  861           throws SQLException {
  862           stmnt.setInt(idx, (val) ? 1 : 0);
  863       }
  864   
  865       /**
  866        * Set the given value as a parameter to the statement.
  867        */
  868       public void setByte(PreparedStatement stmnt, int idx, byte val, Column col)
  869           throws SQLException {
  870           stmnt.setByte(idx, val);
  871       }
  872   
  873       /**
  874        * Set the given value as a parameter to the statement.
  875        */
  876       public void setBytes(PreparedStatement stmnt, int idx, byte[] val,
  877           Column col)
  878           throws SQLException {
  879           if (useSetBytesForBlobs)
  880               stmnt.setBytes(idx, val);
  881           else
  882               setBinaryStream(stmnt, idx, new ByteArrayInputStream(val),
  883                   val.length, col);
  884       }
  885   
  886       /**
  887        * Set the given value as a parameter to the statement.
  888        */
  889       public void setChar(PreparedStatement stmnt, int idx, char val, Column col)
  890           throws SQLException {
  891           if ((col != null && col.isCompatible(Types.INTEGER, null, 0, 0))
  892               || (col == null && storeCharsAsNumbers))
  893               setInt(stmnt, idx, (int) val, col);
  894           else
  895               setString(stmnt, idx, String.valueOf(val), col);
  896       }
  897   
  898       /**
  899        * Set the given value as a parameter to the statement.
  900        */
  901       public void setCharacterStream(PreparedStatement stmnt, int idx,
  902           Reader val, int length, Column col)
  903           throws SQLException {
  904           stmnt.setCharacterStream(idx, val, length);
  905       }
  906   
  907       /**
  908        * Set the given value as a parameter to the statement.
  909        */
  910       public void setClob(PreparedStatement stmnt, int idx, Clob val, Column col)
  911           throws SQLException {
  912           stmnt.setClob(idx, val);
  913       }
  914   
  915       /**
  916        * Set the given value as a parameter to the statement.
  917        */
  918       public void setClobString(PreparedStatement stmnt, int idx, String val,
  919           Column col)
  920           throws SQLException {
  921           if (useSetStringForClobs)
  922               stmnt.setString(idx, val);
  923           else {
  924               // set reader from string
  925               StringReader in = new StringReader(val);
  926               setCharacterStream(stmnt, idx, in, val.length(), col);
  927           }
  928       }
  929   
  930       /**
  931        * Set the given value as a parameter to the statement.
  932        */
  933       public void setDate(PreparedStatement stmnt, int idx, Date val, Column col)
  934           throws SQLException {
  935           if (col != null && col.getType() == Types.DATE)
  936               setDate(stmnt, idx, new java.sql.Date(val.getTime()), null, col);
  937           else if (col != null && col.getType() == Types.TIME)
  938               setTime(stmnt, idx, new Time(val.getTime()), null, col);
  939           else if (val instanceof Timestamp)
  940               setTimestamp(stmnt, idx,(Timestamp) val, null, col);   
  941           else
  942               setTimestamp(stmnt, idx, new Timestamp(val.getTime()), null, col);
  943       }
  944   
  945       /**
  946        * Set the given value as a parameter to the statement.
  947        */
  948       public void setDate(PreparedStatement stmnt, int idx, java.sql.Date val,
  949           Calendar cal, Column col)
  950           throws SQLException {
  951           if (cal == null)
  952               stmnt.setDate(idx, val);
  953           else
  954               stmnt.setDate(idx, val, cal);
  955       }
  956   
  957       /**
  958        * Set the given value as a parameter to the statement.
  959        */
  960       public void setCalendar(PreparedStatement stmnt, int idx, Calendar val,
  961           Column col)
  962           throws SQLException {
  963           // by default we merely delegate the the Date parameter
  964           setDate(stmnt, idx, val.getTime(), col);
  965       }
  966   
  967       /**
  968        * Set the given value as a parameter to the statement.
  969        */
  970       public void setDouble(PreparedStatement stmnt, int idx, double val,
  971           Column col)
  972           throws SQLException {
  973           stmnt.setDouble(idx, val);
  974       }
  975   
  976       /**
  977        * Set the given value as a parameter to the statement.
  978        */
  979       public void setFloat(PreparedStatement stmnt, int idx, float val,
  980           Column col)
  981           throws SQLException {
  982           stmnt.setFloat(idx, val);
  983       }
  984   
  985       /**
  986        * Set the given value as a parameter to the statement.
  987        */
  988       public void setInt(PreparedStatement stmnt, int idx, int val, Column col)
  989           throws SQLException {
  990           stmnt.setInt(idx, val);
  991       }
  992   
  993       /**
  994        * Set the given value as a parameter to the statement.
  995        */
  996       public void setLong(PreparedStatement stmnt, int idx, long val, Column col)
  997           throws SQLException {
  998           stmnt.setLong(idx, val);
  999       }
 1000   
 1001       /**
 1002        * Set the given value as a parameter to the statement.
 1003        */
 1004       public void setLocale(PreparedStatement stmnt, int idx, Locale val,
 1005           Column col)
 1006           throws SQLException {
 1007           setString(stmnt, idx, val.getLanguage() + "_" + val.getCountry()
 1008               + "_" + val.getVariant(), col);
 1009       }
 1010   
 1011       /**
 1012        * Set the given value as a parameters to the statement. The column
 1013        * type will come from {@link Types}.
 1014        */
 1015       public void setNull(PreparedStatement stmnt, int idx, int colType,
 1016           Column col)
 1017           throws SQLException {
 1018           stmnt.setNull(idx, colType);
 1019       }
 1020   
 1021       /**
 1022        * Set the given value as a parameter to the statement.
 1023        */
 1024       public void setNumber(PreparedStatement stmnt, int idx, Number num,
 1025           Column col)
 1026           throws SQLException {
 1027           // check for known floating point types to give driver a chance to
 1028           // handle special numbers like NaN and infinity; bug #1053
 1029           if (num instanceof Double)
 1030               setDouble(stmnt, idx, ((Double) num).doubleValue(), col);
 1031           else if (num instanceof Float)
 1032               setFloat(stmnt, idx, ((Float) num).floatValue(), col);
 1033           else
 1034               setBigDecimal(stmnt, idx, new BigDecimal(num.toString()), col);
 1035       }
 1036   
 1037       /**
 1038        * Set the given value as a parameters to the statement. The column
 1039        * type will come from {@link Types}.
 1040        */
 1041       public void setObject(PreparedStatement stmnt, int idx, Object val,
 1042           int colType, Column col)
 1043           throws SQLException {
 1044           if (colType == -1 || colType == Types.OTHER)
 1045               stmnt.setObject(idx, val);
 1046           else
 1047               stmnt.setObject(idx, val, colType);
 1048       }
 1049   
 1050       /**
 1051        * Set the given value as a parameter to the statement.
 1052        */
 1053       public void setRef(PreparedStatement stmnt, int idx, Ref val, Column col)
 1054           throws SQLException {
 1055           stmnt.setRef(idx, val);
 1056       }
 1057   
 1058       /**
 1059        * Set the given value as a parameter to the statement.
 1060        */
 1061       public void setShort(PreparedStatement stmnt, int idx, short val,
 1062           Column col)
 1063           throws SQLException {
 1064           stmnt.setShort(idx, val);
 1065       }
 1066   
 1067       /**
 1068        * Set the given value as a parameter to the statement.
 1069        */
 1070       public void setString(PreparedStatement stmnt, int idx, String val,
 1071           Column col)
 1072           throws SQLException {
 1073           stmnt.setString(idx, val);
 1074       }
 1075   
 1076       /**
 1077        * Set the given value as a parameter to the statement.
 1078        */
 1079       public void setTime(PreparedStatement stmnt, int idx, Time val,
 1080           Calendar cal, Column col)
 1081           throws SQLException {
 1082           if (cal == null)
 1083               stmnt.setTime(idx, val);
 1084           else
 1085               stmnt.setTime(idx, val, cal);
 1086       }
 1087   
 1088       /**
 1089        * Set the given value as a parameter to the statement.
 1090        */
 1091       public void setTimestamp(PreparedStatement stmnt, int idx,
 1092           Timestamp val, Calendar cal, Column col)
 1093           throws SQLException {
 1094           // ensure that we do not insert dates at a greater precision than
 1095           // that at which they will be returned by a SELECT
 1096           int rounded = (int) Math.round(val.getNanos() /
 1097               (double) datePrecision);
 1098           int nanos = rounded * datePrecision;
 1099           if (nanos > 999999999) {
 1100               // rollover to next second
 1101               val.setTime(val.getTime() + 1000);
 1102               nanos = 0;
 1103           }
 1104   
 1105           if (supportsTimestampNanos)
 1106               val.setNanos(nanos);
 1107           else
 1108               val.setNanos(0);
 1109   
 1110           if (cal == null)
 1111               stmnt.setTimestamp(idx, val);
 1112           else
 1113               stmnt.setTimestamp(idx, val, cal);
 1114       }
 1115   
 1116       /**
 1117        * Set a column value into a prepared statement.
 1118        *
 1119        * @param stmnt the prepared statement to parameterize
 1120        * @param idx the index of the parameter in the prepared statement
 1121        * @param val the value of the column
 1122        * @param col the column being set
 1123        * @param type the field mapping type code for the value
 1124        * @param store the store manager for the current context
 1125        */
 1126       public void setTyped(PreparedStatement stmnt, int idx, Object val,
 1127           Column col, int type, JDBCStore store)
 1128           throws SQLException {
 1129           if (val == null) {
 1130               setNull(stmnt, idx, (col == null) ? Types.OTHER : col.getType(),
 1131                   col);
 1132               return;
 1133           }
 1134   
 1135           Sized s;
 1136           Calendard c;
 1137           switch (type) {
 1138               case JavaTypes.BOOLEAN:
 1139               case JavaTypes.BOOLEAN_OBJ:
 1140                   setBoolean(stmnt, idx, ((Boolean) val).booleanValue(), col);
 1141                   break;
 1142               case JavaTypes.BYTE:
 1143               case JavaTypes.BYTE_OBJ:
 1144                   setByte(stmnt, idx, ((Number) val).byteValue(), col);
 1145                   break;
 1146               case JavaTypes.CHAR:
 1147               case JavaTypes.CHAR_OBJ:
 1148                   setChar(stmnt, idx, ((Character) val).charValue(), col);
 1149                   break;
 1150               case JavaTypes.DOUBLE:
 1151               case JavaTypes.DOUBLE_OBJ:
 1152                   setDouble(stmnt, idx, ((Number) val).doubleValue(), col);
 1153                   break;
 1154               case JavaTypes.FLOAT:
 1155               case JavaTypes.FLOAT_OBJ:
 1156                   setFloat(stmnt, idx, ((Number) val).floatValue(), col);
 1157                   break;
 1158               case JavaTypes.INT:
 1159               case JavaTypes.INT_OBJ:
 1160                   setInt(stmnt, idx, ((Number) val).intValue(), col);
 1161                   break;
 1162               case JavaTypes.LONG:
 1163               case JavaTypes.LONG_OBJ:
 1164                   setLong(stmnt, idx, ((Number) val).longValue(), col);
 1165                   break;
 1166               case JavaTypes.SHORT:
 1167               case JavaTypes.SHORT_OBJ:
 1168                   setShort(stmnt, idx, ((Number) val).shortValue(), col);
 1169                   break;
 1170               case JavaTypes.STRING:
 1171                   if (col != null && (col.getType() == Types.CLOB
 1172                       || col.getType() == Types.LONGVARCHAR))
 1173                       setClobString(stmnt, idx, (String) val, col);
 1174                   else
 1175                       setString(stmnt, idx, (String) val, col);
 1176                   break;
 1177               case JavaTypes.OBJECT:
 1178                   setBlobObject(stmnt, idx, val, col, store);
 1179                   break;
 1180               case JavaTypes.DATE:
 1181                   setDate(stmnt, idx, (Date) val, col);
 1182                   break;
 1183               case JavaTypes.CALENDAR:
 1184                   setCalendar(stmnt, idx, (Calendar) val, col);
 1185                   break;
 1186               case JavaTypes.BIGDECIMAL:
 1187                   setBigDecimal(stmnt, idx, (BigDecimal) val, col);
 1188                   break;
 1189               case JavaTypes.BIGINTEGER:
 1190                   setBigInteger(stmnt, idx, (BigInteger) val, col);
 1191                   break;
 1192               case JavaTypes.NUMBER:
 1193                   setNumber(stmnt, idx, (Number) val, col);
 1194                   break;
 1195               case JavaTypes.LOCALE:
 1196                   setLocale(stmnt, idx, (Locale) val, col);
 1197                   break;
 1198               case JavaSQLTypes.SQL_ARRAY:
 1199                   setArray(stmnt, idx, (Array) val, col);
 1200                   break;
 1201               case JavaSQLTypes.ASCII_STREAM:
 1202                   s = (Sized) val;
 1203                   setAsciiStream(stmnt, idx, (InputStream) s.value, s.size, col);
 1204                   break;
 1205               case JavaSQLTypes.BINARY_STREAM:
 1206                   s = (Sized) val;
 1207                   setBinaryStream(stmnt, idx, (InputStream) s.value, s.size, col);
 1208                   break;
 1209               case JavaSQLTypes.BLOB:
 1210                   setBlob(stmnt, idx, (Blob) val, col);
 1211                   break;
 1212               case JavaSQLTypes.BYTES:
 1213                   setBytes(stmnt, idx, (byte[]) val, col);
 1214                   break;
 1215               case JavaSQLTypes.CHAR_STREAM:
 1216                   s = (Sized) val;
 1217                   setCharacterStream(stmnt, idx, (Reader) s.value, s.size, col);
 1218                   break;
 1219               case JavaSQLTypes.CLOB:
 1220                   setClob(stmnt, idx, (Clob) val, col);
 1221                   break;
 1222               case JavaSQLTypes.SQL_DATE:
 1223                   if (val instanceof Calendard) {
 1224                       c = (Calendard) val;
 1225                       setDate(stmnt, idx, (java.sql.Date) c.value, c.calendar,
 1226                           col);
 1227                   } else
 1228                       setDate(stmnt, idx, (java.sql.Date) val, null, col);
 1229                   break;
 1230               case JavaSQLTypes.REF:
 1231                   setRef(stmnt, idx, (Ref) val, col);
 1232                   break;
 1233               case JavaSQLTypes.TIME:
 1234                   if (val instanceof Calendard) {
 1235                       c = (Calendard) val;
 1236                       setTime(stmnt, idx, (Time) c.value, c.calendar, col);
 1237                   } else
 1238                       setTime(stmnt, idx, (Time) val, null, col);
 1239                   break;
 1240               case JavaSQLTypes.TIMESTAMP:
 1241                   if (val instanceof Calendard) {
 1242                       c = (Calendard) val;
 1243                       setTimestamp(stmnt, idx, (Timestamp) c.value, c.calendar,
 1244                           col);
 1245                   } else
 1246                       setTimestamp(stmnt, idx, (Timestamp) val, null, col);
 1247                   break;
 1248               default:
 1249                   if (col != null && (col.getType() == Types.BLOB
 1250                       || col.getType() == Types.VARBINARY))
 1251                       setBlobObject(stmnt, idx, val, col, store);
 1252                   else
 1253                       setObject(stmnt, idx, val, col.getType(), col);
 1254           }
 1255       }
 1256   
 1257       /**
 1258        * Set a completely unknown parameter into a prepared statement.
 1259        */
 1260       public void setUnknown(PreparedStatement stmnt, int idx, Object val,
 1261           Column col)
 1262           throws SQLException {
 1263           Sized sized = null;
 1264           Calendard cald = null;
 1265           if (val instanceof Sized) {
 1266               sized = (Sized) val;
 1267               val = sized.value;
 1268           } else if (val instanceof Calendard) {
 1269               cald = (Calendard) val;
 1270               val = cald.value;
 1271           }
 1272   
 1273           if (val == null)
 1274               setNull(stmnt, idx, (col == null) ? Types.OTHER : col.getType(),
 1275                   col);
 1276           else if (val instanceof String)
 1277               setString(stmnt, idx, val.toString(), col);
 1278           else if (val instanceof Integer)
 1279               setInt(stmnt, idx, ((Integer) val).intValue(), col);
 1280           else if (val instanceof Boolean)
 1281               setBoolean(stmnt, idx, ((Boolean) val).booleanValue(), col);
 1282           else if (val instanceof Long)
 1283               setLong(stmnt, idx, ((Long) val).longValue(), col);
 1284           else if (val instanceof Float)
 1285               setFloat(stmnt, idx, ((Float) val).floatValue(), col);
 1286           else if (val instanceof Double)
 1287               setDouble(stmnt, idx, ((Double) val).doubleValue(), col);
 1288           else if (val instanceof Byte)
 1289               setByte(stmnt, idx, ((Byte) val).byteValue(), col);
 1290           else if (val instanceof Character)
 1291               setChar(stmnt, idx, ((Character) val).charValue(), col);
 1292           else if (val instanceof Short)
 1293               setShort(stmnt, idx, ((Short) val).shortValue(), col);
 1294           else if (val instanceof Locale)
 1295               setLocale(stmnt, idx, (Locale) val, col);
 1296           else if (val instanceof BigDecimal)
 1297               setBigDecimal(stmnt, idx, (BigDecimal) val, col);
 1298           else if (val instanceof BigInteger)
 1299               setBigInteger(stmnt, idx, (BigInteger) val, col);
 1300           else if (val instanceof Array)
 1301               setArray(stmnt, idx, (Array) val, col);
 1302           else if (val instanceof Blob)
 1303               setBlob(stmnt, idx, (Blob) val, col);
 1304           else if (val instanceof byte[])
 1305               setBytes(stmnt, idx, (byte[]) val, col);
 1306           else if (val instanceof Clob)
 1307               setClob(stmnt, idx, (Clob) val, col);
 1308           else if (val instanceof Ref)
 1309               setRef(stmnt, idx, (Ref) val, col);
 1310           else if (val instanceof java.sql.Date)
 1311               setDate(stmnt, idx, (java.sql.Date) val,
 1312                   (cald == null) ? null : cald.calendar, col);
 1313           else if (val instanceof Timestamp)
 1314               setTimestamp(stmnt, idx, (Timestamp) val,
 1315                   (cald == null) ? null : cald.calendar, col);
 1316           else if (val instanceof Time)
 1317               setTime(stmnt, idx, (Time) val,
 1318                   (cald == null) ? null : cald.calendar, col);
 1319           else if (val instanceof Date)
 1320               setDate(stmnt, idx, (Date) val, col);
 1321           else if (val instanceof Calendar)
 1322               setDate(stmnt, idx, ((Calendar) val).getTime(), col);
 1323           else if (val instanceof Reader)
 1324               setCharacterStream(stmnt, idx, (Reader) val,
 1325                   (sized == null) ? 0 : sized.size, col);
 1326           else
 1327               throw new UserException(_loc.get("bad-param", val.getClass()));
 1328       }
 1329   
 1330       /**
 1331        * Return the serialized bytes for the given object.
 1332        */
 1333       public byte[] serialize(Object val, JDBCStore store)
 1334           throws SQLException {
 1335           if (val == null)
 1336               return null;
 1337           if (val instanceof SerializedData)
 1338               return ((SerializedData) val).bytes;
 1339           return Serialization.serialize(val, store.getContext());
 1340       }
 1341   
 1342       /**
 1343        * Invoke the JDK 1.4 <code>setBytes</code> method on the given BLOB object.
 1344        */
 1345       public void putBytes(Object blob, byte[] data)
 1346           throws SQLException {
 1347           if (_setBytes == null) {
 1348               try {
 1349                   _setBytes = blob.getClass().getMethod("setBytes",
 1350                       new Class[]{ long.class, byte[].class });
 1351               } catch (Exception e) {
 1352                   throw new StoreException(e);
 1353               }
 1354           }
 1355           invokePutLobMethod(_setBytes, blob,
 1356               new Object[]{ Numbers.valueOf(1L), data });
 1357       }
 1358   
 1359       /**
 1360        * Invoke the JDK 1.4 <code>setString</code> method on the given CLOB
 1361        * object.
 1362        */
 1363       public void putString(Object clob, String data)
 1364           throws SQLException {
 1365           if (_setString == null) {
 1366               try {
 1367                   _setString = clob.getClass().getMethod("setString",
 1368                       new Class[]{ long.class, String.class });
 1369               } catch (Exception e) {
 1370                   throw new StoreException(e);
 1371               }
 1372           }
 1373           invokePutLobMethod(_setString, clob,
 1374               new Object[]{ Numbers.valueOf(1L), data });
 1375       }
 1376   
 1377       /**
 1378        * Invoke the JDK 1.4 <code>setCharacterStream</code> method on the given
 1379        * CLOB object.
 1380        */
 1381       public void putChars(Object clob, char[] data)
 1382           throws SQLException {
 1383           if (_setCharStream == null) {
 1384               try {
 1385                   _setCharStream = clob.getClass().getMethod
 1386                       ("setCharacterStream", new Class[]{ long.class });
 1387               } catch (Exception e) {
 1388                   throw new StoreException(e);
 1389               }
 1390           }
 1391   
 1392           Writer writer = (Writer) invokePutLobMethod(_setCharStream, clob,
 1393               new Object[]{ Numbers.valueOf(1L) });
 1394           try {
 1395               writer.write(data);
 1396               writer.flush();
 1397           } catch (IOException ioe) {
 1398               throw new SQLException(ioe.toString());
 1399           }
 1400       }
 1401   
 1402       /**
 1403        * Invoke the given LOB method on the given target with the given data.
 1404        */
 1405       private static Object invokePutLobMethod(Method method, Object target,
 1406           Object[] args)
 1407           throws SQLException {
 1408           try {
 1409               return method.invoke(target, args);
 1410           } catch (InvocationTargetException ite) {
 1411               Throwable t = ite.getTargetException();
 1412               if (t instanceof SQLException)
 1413                   throw(SQLException) t;
 1414               throw new StoreException(t);
 1415           } catch (Exception e) {
 1416               throw new StoreException(e);
 1417           }
 1418       }
 1419   
 1420       /**
 1421        * Warn that a particular value could not be stored precisely.
 1422        * After the first warning for a particular type, messages
 1423        * will be turned into trace messages.
 1424        */
 1425       protected void storageWarning(Object orig, Object converted) {
 1426           boolean warn;
 1427           synchronized (this) {
 1428               if (_precisionWarnedTypes == null)
 1429                   _precisionWarnedTypes = new HashSet();
 1430               warn = _precisionWarnedTypes.add(orig.getClass());
 1431           }
 1432   
 1433           if (storageLimitationsFatal || (warn && log.isWarnEnabled())
 1434               || (!warn && log.isTraceEnabled())) {
 1435               Message msg = _loc.get("storage-restriction", new Object[]{
 1436                   platform,
 1437                   orig,
 1438                   orig.getClass().getName(),
 1439                   converted,
 1440               });
 1441   
 1442               if (storageLimitationsFatal)
 1443                   throw new StoreException(msg);
 1444   
 1445               if (warn)
 1446                   log.warn(msg);
 1447               else
 1448                   log.trace(msg);
 1449           }
 1450       }
 1451   
 1452       /////////
 1453       // Types
 1454       /////////
 1455   
 1456       /**
 1457        * Return the preferred {@link Types} constant for the given
 1458        * {@link JavaTypes} or {@link JavaSQLTypes} constant.
 1459        */
 1460       public int getJDBCType(int metaTypeCode, boolean lob) {
 1461           if (lob) {
 1462               switch (metaTypeCode) {
 1463                   case JavaTypes.STRING:
 1464                   case JavaSQLTypes.ASCII_STREAM:
 1465                   case JavaSQLTypes.CHAR_STREAM:
 1466                       return getPreferredType(Types.CLOB);
 1467                   default:
 1468                       return getPreferredType(Types.BLOB);
 1469               }
 1470           }
 1471   
 1472           switch (metaTypeCode) {
 1473               case JavaTypes.BOOLEAN:
 1474               case JavaTypes.BOOLEAN_OBJ:
 1475                   return getPreferredType(Types.BIT);
 1476               case JavaTypes.BYTE:
 1477               case JavaTypes.BYTE_OBJ:
 1478                   return getPreferredType(Types.TINYINT);
 1479               case JavaTypes.CHAR:
 1480               case JavaTypes.CHAR_OBJ:
 1481                   if (storeCharsAsNumbers)
 1482                       return getPreferredType(Types.INTEGER);
 1483                   return getPreferredType(Types.CHAR);
 1484               case JavaTypes.DOUBLE:
 1485               case JavaTypes.DOUBLE_OBJ:
 1486                   return getPreferredType(Types.DOUBLE);
 1487               case JavaTypes.FLOAT:
 1488               case JavaTypes.FLOAT_OBJ:
 1489                   return getPreferredType(Types.REAL);
 1490               case JavaTypes.INT:
 1491               case JavaTypes.INT_OBJ:
 1492                   return getPreferredType(Types.INTEGER);
 1493               case JavaTypes.LONG:
 1494               case JavaTypes.LONG_OBJ:
 1495                   return getPreferredType(Types.BIGINT);
 1496               case JavaTypes.SHORT:
 1497               case JavaTypes.SHORT_OBJ:
 1498                   return getPreferredType(Types.SMALLINT);
 1499               case JavaTypes.STRING:
 1500               case JavaTypes.LOCALE:
 1501               case JavaSQLTypes.ASCII_STREAM:
 1502               case JavaSQLTypes.CHAR_STREAM:
 1503                   return getPreferredType(Types.VARCHAR);
 1504               case JavaTypes.BIGINTEGER:
 1505                   if (storeLargeNumbersAsStrings)
 1506                       return getPreferredType(Types.VARCHAR);
 1507                   return getPreferredType(Types.BIGINT);
 1508               case JavaTypes.BIGDECIMAL:
 1509                   if (storeLargeNumbersAsStrings)
 1510                       return getPreferredType(Types.VARCHAR);
 1511                   return getPreferredType(Types.DOUBLE);
 1512               case JavaTypes.NUMBER:
 1513                   if (storeLargeNumbersAsStrings)
 1514                       return getPreferredType(Types.VARCHAR);
 1515                   return getPreferredType(Types.NUMERIC);
 1516               case JavaTypes.CALENDAR:
 1517               case JavaTypes.DATE:
 1518                   return getPreferredType(Types.TIMESTAMP);
 1519               case JavaSQLTypes.SQL_ARRAY:
 1520                   return getPreferredType(Types.ARRAY);
 1521               case JavaSQLTypes.BINARY_STREAM:
 1522               case JavaSQLTypes.BLOB:
 1523               case JavaSQLTypes.BYTES:
 1524                   return getPreferredType(Types.BLOB);
 1525               case JavaSQLTypes.CLOB:
 1526                   return getPreferredType(Types.CLOB);
 1527               case JavaSQLTypes.SQL_DATE:
 1528                   return getPreferredType(Types.DATE);
 1529               case JavaSQLTypes.TIME:
 1530                   return getPreferredType(Types.TIME);
 1531               case JavaSQLTypes.TIMESTAMP:
 1532                   return getPreferredType(Types.TIMESTAMP);
 1533               default:
 1534                   return getPreferredType(Types.BLOB);
 1535           }
 1536       }
 1537   
 1538       /**
 1539        * Return the preferred {@link Types} type for the given one. Returns
 1540        * the given type by default.
 1541        */
 1542       public int getPreferredType(int type) {
 1543           return type;
 1544       }
 1545   
 1546       /**
 1547        * Return the preferred database type name for the given column's type
 1548        * from {@link Types}.
 1549        */
 1550       public String getTypeName(Column col) {
 1551           if (!StringUtils.isEmpty(col.getTypeName()))
 1552               return appendSize(col, col.getTypeName());
 1553   
 1554           if (col.isAutoAssigned() && autoAssignTypeName != null)
 1555               return appendSize(col, autoAssignTypeName);
 1556   
 1557           return appendSize(col, getTypeName(col.getType()));
 1558       }
 1559   
 1560       /**
 1561        * Returns the type name for the specific constant as defined
 1562        * by {@link java.sql.Types}.
 1563        *
 1564        * @param type the type
 1565        * @return the name for the type
 1566        */
 1567       public String getTypeName(int type) {
 1568           switch (type) {
 1569               case Types.ARRAY:
 1570                   return arrayTypeName;
 1571               case Types.BIGINT:
 1572                   return bigintTypeName;
 1573               case Types.BINARY:
 1574                   return binaryTypeName;
 1575               case Types.BIT:
 1576                   return bitTypeName;
 1577               case Types.BLOB:
 1578                   return blobTypeName;
 1579               case Types.BOOLEAN:
 1580                   return booleanTypeName;
 1581               case Types.CHAR:
 1582                   return charTypeName;
 1583               case Types.CLOB:
 1584                   return clobTypeName;
 1585               case Types.DATE:
 1586                   return dateTypeName;
 1587               case Types.DECIMAL:
 1588                   return decimalTypeName;
 1589               case Types.DISTINCT:
 1590                   return distinctTypeName;
 1591               case Types.DOUBLE:
 1592                   return doubleTypeName;
 1593               case Types.FLOAT:
 1594                   return floatTypeName;
 1595               case Types.INTEGER:
 1596                   return integerTypeName;
 1597               case Types.JAVA_OBJECT:
 1598                   return javaObjectTypeName;
 1599               case Types.LONGVARBINARY:
 1600                   return longVarbinaryTypeName;
 1601               case Types.LONGVARCHAR:
 1602                   return longVarcharTypeName;
 1603               case Types.NULL:
 1604                   return nullTypeName;
 1605               case Types.NUMERIC:
 1606                   return numericTypeName;
 1607               case Types.OTHER:
 1608                   return otherTypeName;
 1609               case Types.REAL:
 1610                   return realTypeName;
 1611               case Types.REF:
 1612                   return refTypeName;
 1613               case Types.SMALLINT:
 1614                   return smallintTypeName;
 1615               case Types.STRUCT:
 1616                   return structTypeName;
 1617               case Types.TIME:
 1618                   return timeTypeName;
 1619               case Types.TIMESTAMP:
 1620                   return timestampTypeName;
 1621               case Types.TINYINT:
 1622                   return tinyintTypeName;
 1623               case Types.VARBINARY:
 1624                   return varbinaryTypeName;
 1625               case Types.VARCHAR:
 1626                   return varcharTypeName;
 1627               default:
 1628                   return otherTypeName;
 1629           }
 1630       }
 1631   
 1632       /**
 1633        * Helper method to add size properties to the specified type.
 1634        * If present, the string "{0}" will be replaced with the size definition;
 1635        * otherwise the size definition will be appended to the type name.
 1636        * If your database has column types that don't allow size definitions,
 1637        * override this method to return the unaltered type name for columns of
 1638        * those types (or add the type names to the
 1639        * <code>fixedSizeTypeNameSet</code>).
 1640        * 
 1641        * <P>Some databases support "type modifiers" for example the unsigned
 1642        * "modifier" in MySQL. In these cases the size should go between the type 
 1643        * and the "modifier", instead of after the modifier. For example 
 1644        * CREATE table FOO ( myint INT (10) UNSIGNED . . .) instead of 
 1645        * CREATE table FOO ( myint INT UNSIGNED (10) . . .).
 1646        * Type modifiers should be added to <code>typeModifierSet</code> in 
 1647        * subclasses. 
 1648        */
 1649       protected String appendSize(Column col, String typeName) {
 1650           if (fixedSizeTypeNameSet.contains(typeName.toUpperCase()))
 1651               return typeName;
 1652           if (typeName.indexOf('(') != -1)
 1653               return typeName;
 1654   
 1655           String size = null;
 1656           if (col.getSize() > 0) {
 1657               StringBuffer buf = new StringBuffer(10);
 1658               buf.append("(").append(col.getSize());
 1659               if (col.getDecimalDigits() > 0)
 1660                   buf.append(", ").append(col.getDecimalDigits());
 1661               buf.append(")");
 1662               size = buf.toString();
 1663           }
 1664   
 1665           return insertSize(typeName, size);
 1666       }
 1667   
 1668       /**
 1669        * Helper method that inserts a size clause for a given SQL type. 
 1670        * 
 1671        * @see appendSize
 1672        * 
 1673        * @param typeName  The SQL type ie INT
 1674        * @param size      The size clause ie (10)
 1675        * @return          The typeName + size clause. Usually the size clause will 
 1676        *                  be appended to typeName. If the typeName contains a 
 1677        *                  marker : {0} or if typeName contains a modifier the 
 1678        *                  size clause will be inserted appropriately.   
 1679        */
 1680       protected String insertSize(String typeName, String size) {
 1681       	if (StringUtils.isEmpty(size)) {
 1682               int idx = typeName.indexOf("{0}");
 1683               if (idx != -1) {
 1684                   return typeName.substring(0, idx);
 1685               }
 1686               return typeName;
 1687           }
 1688       	
 1689           int idx = typeName.indexOf("{0}");
 1690           if (idx != -1) {
 1691               // replace '{0}' with size
 1692               String ret = typeName.substring(0, idx);
 1693               if (size != null)
 1694                   ret = ret + size;
 1695               if (typeName.length() > idx + 3)
 1696                   ret = ret + typeName.substring(idx + 3);
 1697               return ret;
 1698           }
 1699           if (!typeModifierSet.isEmpty()) {
 1700               String s;
 1701               idx = typeName.length();
 1702               int curIdx = -1;
 1703               for (Iterator i = typeModifierSet.iterator(); i.hasNext();) {
 1704                   s = (String) i.next();
 1705                   if (typeName.toUpperCase().indexOf(s) != -1) {
 1706                       curIdx = typeName.toUpperCase().indexOf(s);
 1707                       if (curIdx != -1 && curIdx < idx) {
 1708                           idx = curIdx;
 1709                       }
 1710                   }
 1711               }
 1712               if(idx != typeName.length()) {
 1713                   String ret = typeName.substring(0, idx);
 1714                   ret = ret + size;
 1715                   ret = ret + ' ' + typeName.substring(idx);
 1716                   return ret;
 1717               }
 1718           }
 1719           return typeName + size;
 1720       }
 1721   
 1722       ///////////
 1723       // Selects
 1724       ///////////
 1725   
 1726       /**
 1727        * Set the name of the join syntax to use: sql92, traditional, database
 1728        */
 1729       public void setJoinSyntax(String syntax) {
 1730           if ("sql92".equals(syntax))
 1731               joinSyntax = SYNTAX_SQL92;
 1732           else if ("traditional".equals(syntax))
 1733               joinSyntax = SYNTAX_TRADITIONAL;
 1734           else if ("database".equals(syntax))
 1735               joinSyntax = SYNTAX_DATABASE;
 1736           else if (!StringUtils.isEmpty(syntax))
 1737               throw new IllegalArgumentException(syntax);
 1738       }
 1739   
 1740       /**
 1741        * Return a SQL string to act as a placeholder for the given column.
 1742        */
 1743       public String getPlaceholderValueString(Column col) {
 1744           switch (col.getType()) {
 1745               case Types.BIGINT:
 1746               case Types.BIT:
 1747               case Types.INTEGER:
 1748               case Types.NUMERIC:
 1749               case Types.SMALLINT:
 1750               case Types.TINYINT:
 1751                   return "0";
 1752               case Types.CHAR:
 1753                   return (storeCharsAsNumbers) ? "0" : "' '";
 1754               case Types.CLOB:
 1755               case Types.LONGVARCHAR:
 1756               case Types.VARCHAR:
 1757                   return "''";
 1758               case Types.DATE:
 1759                   return ZERO_DATE_STR;
 1760               case Types.DECIMAL:
 1761               case Types.DOUBLE:
 1762               case Types.FLOAT:
 1763               case Types.REAL:
 1764                   return "0.0";
 1765               case Types.TIME:
 1766                   return ZERO_TIME_STR;
 1767               case Types.TIMESTAMP:
 1768                   return ZERO_TIMESTAMP_STR;
 1769               default:
 1770                   return "NULL";
 1771           }
 1772       }
 1773   
 1774       /**
 1775        * Create a SELECT COUNT statement in the proper join syntax for the
 1776        * given instance.
 1777        */
 1778       public SQLBuffer toSelectCount(Select sel) {
 1779           SQLBuffer selectSQL = new SQLBuffer(this);
 1780           SQLBuffer from;
 1781           sel.addJoinClassConditions();
 1782           if (sel.getFromSelect() != null)
 1783               from = getFromSelect(sel, false);
 1784           else
 1785               from = getFrom(sel, false);
 1786           SQLBuffer where = getWhere(sel, false);
 1787   
 1788           // if no grouping and no range, we might be able to get by without
 1789           // a subselect
 1790           if (sel.getGrouping() == null && sel.getStartIndex() == 0
 1791               && sel.getEndIndex() == Long.MAX_VALUE) {
 1792               // if the select has no identifier cols, use COUNT(*)
 1793               List aliases = (!sel.isDistinct()) ? Collections.EMPTY_LIST
 1794                   : sel.getIdentifierAliases();
 1795               if (aliases.isEmpty()) {
 1796                   selectSQL.append("COUNT(*)");
 1797                   return toSelect(selectSQL, null, from, where, null, null, null,
 1798                       false, false, 0, Long.MAX_VALUE);
 1799               }
 1800   
 1801               // if there is a single distinct col, use COUNT(DISTINCT col)
 1802               if (aliases.size() == 1) {
 1803                   selectSQL.append("COUNT(DISTINCT ").
 1804                       append(aliases.get(0).toString()).append(")");
 1805                   return toSelect(selectSQL, null, from, where, null, null, null,
 1806                       false, false, 0, Long.MAX_VALUE);
 1807               }
 1808   
 1809               // can we combine distinct cols?
 1810               if (distinctCountColumnSeparator != null) {
 1811                   selectSQL.append("COUNT(DISTINCT ");
 1812                   for (int i = 0; i < aliases.size(); i++) {
 1813                       if (i > 0) {
 1814                           selectSQL.append(" ");
 1815                           selectSQL.append(distinctCountColumnSeparator);
 1816                           selectSQL.append(" ");
 1817                       }
 1818                       selectSQL.append(aliases.get(i).toString());
 1819                   }
 1820                   selectSQL.append(")");
 1821                   return toSelect(selectSQL, null, from, where, null, null, null,
 1822                       false, false, 0, Long.MAX_VALUE);
 1823               }
 1824           }
 1825   
 1826           // since we can't combine distinct cols, we have to perform an outer
 1827           // COUNT(*) select using the original select as a subselect in the
 1828           // FROM clause
 1829           assertSupport(supportsSubselect, "SupportsSubselect");
 1830   
 1831           SQLBuffer subSelect = getSelects(sel, true, false);
 1832           SQLBuffer subFrom = from;
 1833           from = new SQLBuffer(this);
 1834           from.append("(");
 1835           from.append(toSelect(subSelect, null, subFrom, where,
 1836               sel.getGrouping(), sel.getHaving(), null, sel.isDistinct(),
 1837               false, sel.getStartIndex(), sel.getEndIndex(), true));
 1838           from.append(")");
 1839           if (requiresAliasForSubselect)
 1840               from.append(" ").append(Select.FROM_SELECT_ALIAS);
 1841   
 1842           selectSQL.append("COUNT(*)");
 1843           return toSelect(selectSQL, null, from, null, null, null, null,
 1844               false, false, 0, Long.MAX_VALUE);
 1845       }
 1846   
 1847       /**
 1848        * Create a DELETE statement for the specified Select. If the
 1849        * database does not support the bulk delete statement (such as
 1850        * cases where a subselect is required and the database doesn't support
 1851        * subselects), this method should return null.
 1852        */
 1853       public SQLBuffer toDelete(ClassMapping mapping, Select sel, 
 1854           Object[] params) {
 1855           return toBulkOperation(mapping, sel, null, params, null);
 1856       }
 1857   
 1858       public SQLBuffer toUpdate(ClassMapping mapping, Select sel,
 1859           JDBCStore store, Object[] params, Map updates) {
 1860           return toBulkOperation(mapping, sel, store, params, updates);
 1861       }
 1862   
 1863       /**
 1864        * Returns the SQL for a bulk operation, either a DELETE or an UPDATE.
 1865        *
 1866        * @param mapping the mappng against which we are operating
 1867        * @param sel the Select that will constitute the WHERE clause
 1868        * @param store the current store
 1869        * @param updateParams the Map that holds the update parameters; a null
 1870        * value indicates that this is a delete operation
 1871        * @return the SQLBuffer for the update, or <em>null</em> if it is not
 1872        * possible to perform the bulk update
 1873        */
 1874       protected SQLBuffer toBulkOperation(ClassMapping mapping, Select sel,
 1875           JDBCStore store, Object[] params, Map updateParams) {
 1876           SQLBuffer sql = new SQLBuffer(this);
 1877           if (updateParams == null) {
 1878             if (requiresTargetForDelete) {
 1879               sql.append("DELETE ");
 1880               SQLBuffer deleteTargets = getDeleteTargets(sel);
 1881               sql.append(deleteTargets);
 1882               sql.append(" FROM ");
 1883             } else {
 1884               sql.append("DELETE FROM ");
 1885             }
 1886           }
 1887           else
 1888               sql.append("UPDATE ");
 1889           sel.addJoinClassConditions();
 1890   
 1891           // if there is only a single table in the select, then we can
 1892           // just issue a single DELETE FROM TABLE WHERE <conditions>
 1893           // statement; otherwise, since SQL doesn't allow deleting
 1894           // from one of a multi-table select, we need to issue a subselect
 1895           // like DELETE FROM TABLE WHERE EXISTS
 1896           // (SELECT 1 FROM TABLE t0 WHERE t0.ID = TABLE.ID); also, some
 1897           // databases do not allow aliases in delete statements, which
 1898           // also causes us to use a subselect
 1899           if (sel.getTableAliases().size() == 1 && supportsSubselect
 1900               && allowsAliasInBulkClause) {
 1901               SQLBuffer from;
 1902               if (sel.getFromSelect() != null)
 1903                   from = getFromSelect(sel, false);
 1904               else
 1905                   from = getFrom(sel, false);
 1906   
 1907               sql.append(from);
 1908               appendUpdates(sel, store, sql, params, updateParams,
 1909                   allowsAliasInBulkClause);
 1910   
 1911               SQLBuffer where = sel.getWhere();
 1912               if (where != null && !where.isEmpty()) {
 1913                   sql.append(" WHERE ");
 1914                   sql.append(where);
 1915               }
 1916               return sql;
 1917           }
 1918   
 1919           Table table = mapping.getTable();
 1920           String tableName = getFullName(table, false);
 1921   
 1922           // only use a  subselect if the where is not empty; otherwise
 1923           // an unqualified delete or update will work
 1924           if (sel.getWhere() == null || sel.getWhere().isEmpty()) {
 1925               sql.append(tableName);
 1926               appendUpdates(sel, store, sql, params, updateParams, false);
 1927               return sql;
 1928           }
 1929   
 1930           // we need to use a subselect if we are to bulk delete where
 1931           // the select includes multiple tables; if the database
 1932           // doesn't support it, then we need to sigal this by returning null
 1933           if (!supportsSubselect || !supportsCorrelatedSubselect)
 1934               return null;
 1935   
 1936           Column[] pks = mapping.getPrimaryKeyColumns();
 1937           sel.clearSelects();
 1938           sel.setDistinct(true);
 1939   
 1940           // if we have only a single PK, we can use a non-correlated
 1941           // subquery (using an IN statement), which is much faster than
 1942           // a correlated subquery (since a correlated subquery needs
 1943           // to be executed once for each row in the table)
 1944           if (pks.length == 1) {
 1945               sel.select(pks[0]);
 1946               sql.append(tableName);
 1947               appendUpdates(sel, store, sql, params, updateParams, false);
 1948               sql.append(" WHERE ").
 1949                   append(pks[0]).append(" IN (").
 1950                   append(sel.toSelect(false, null)).append(")");
 1951           } else {
 1952               sel.clearSelects();
 1953               sel.setDistinct(false);
 1954   
 1955               // since the select is using a correlated subquery, we
 1956               // only need to select a bogus virtual column
 1957               sel.select("1", null);
 1958   
 1959               // add in the joins to the table
 1960               Column[] cols = table.getPrimaryKey().getColumns();
 1961               SQLBuffer buf = new SQLBuffer(this);
 1962               buf.append("(");
 1963               for (int i = 0; i < cols.length; i++) {
 1964                   if (i > 0)
 1965                       buf.append(" AND ");
 1966   
 1967                   // add in "t0.PK = MYTABLE.PK"
 1968                   buf.append(sel.getColumnAlias(cols[i], null)).append(" = ").
 1969                       append(table).append(catalogSeparator).append(cols[i]);
 1970               }
 1971               buf.append(")");
 1972               sel.where(buf, null);
 1973   
 1974               sql.append(tableName);
 1975               appendUpdates(sel, store, sql, params, updateParams, false);
 1976               sql.append(" WHERE EXISTS (").
 1977                   append(sel.toSelect(false, null)).append(")");
 1978           }
 1979           return sql;
 1980       }
 1981   
 1982       protected SQLBuffer getDeleteTargets(Select sel) {
 1983         SQLBuffer deleteTargets = new SQLBuffer(this);
 1984         Collection aliases = sel.getTableAliases();
 1985         // Assumes aliases are of the form "TABLENAME t0"
 1986         for (Iterator itr = aliases.iterator(); itr.hasNext();) {
 1987           String tableAlias = itr.next().toString();
 1988           int spaceIndex = tableAlias.indexOf(' ');
 1989           if (spaceIndex > 0 && spaceIndex < tableAlias.length() - 1) {
 1990             if (allowsAliasInBulkClause) {
 1991               deleteTargets.append(tableAlias.substring(spaceIndex + 1));
 1992             } else {
 1993               deleteTargets.append(tableAlias.substring(0, spaceIndex));
 1994             }
 1995           } else {
 1996             deleteTargets.append(tableAlias);
 1997           }
 1998           if (itr.hasNext())
 1999             deleteTargets.append(", ");
 2000         }      
 2001         return deleteTargets;      
 2002       }
 2003   
 2004       protected void appendUpdates(Select sel, JDBCStore store, SQLBuffer sql,
 2005           Object[] params, Map updateParams, boolean allowAlias) {
 2006           if (updateParams == null || updateParams.size() == 0)
 2007               return;
 2008   
 2009           // manually build up the SET clause for the UPDATE statement
 2010           sql.append(" SET ");
 2011           ExpContext ctx = new ExpContext(store, params, 
 2012               store.getFetchConfiguration());
 2013   
 2014           // If the updates map contains any version fields, assume that the
 2015           // optimistic lock version data is being handled properly by the
 2016           // caller. Otherwise, give the version indicator an opportunity to
 2017           // add more update clauses as needed.
 2018           boolean augmentUpdates = true;
 2019   
 2020           for (Iterator i = updateParams.entrySet().iterator(); i.hasNext();) {
 2021               Map.Entry next = (Map.Entry) i.next();
 2022               Path path = (Path) next.getKey();
 2023               FieldMapping fmd = (FieldMapping) path.last();
 2024   
 2025               if (fmd.isVersion())
 2026                   augmentUpdates = false;
 2027   
 2028               Val val = (Val) next.getValue();
 2029   
 2030               Column col = fmd.getColumns()[0];
 2031               if (allowAlias) {
 2032                 sql.append(sel.getColumnAlias(col));
 2033               } else {
 2034                 sql.append(col.getName());  
 2035               }            
 2036               sql.append(" = ");
 2037   
 2038               ExpState state = val.initialize(sel, ctx, 0);
 2039               // JDBC Paths are always PCPaths; PCPath implements Val
 2040               ExpState pathState = ((Val) path).initialize(sel, ctx, 0);
 2041               calculateValue(val, sel, ctx, state, path, pathState);
 2042   
 2043               // append the value with a null for the Select; i
 2044               // indicates that the
 2045               int length = val.length(sel, ctx, state);
 2046               for (int j = 0; j < length; j++)
 2047                   val.appendTo((allowAlias) ? sel : null, ctx, state, sql, j);
 2048   
 2049               if (i.hasNext())
 2050                   sql.append(", ");
 2051           }
 2052   
 2053           if (augmentUpdates) {
 2054               Path path = (Path) updateParams.keySet().iterator().next();
 2055               FieldMapping fm = (FieldMapping) path.last();
 2056               ClassMapping meta = fm.getDeclaringMapping();
 2057               Map updates = meta.getVersion().getBulkUpdateValues();
 2058               for (Iterator iter = updates.entrySet().iterator();
 2059                   iter.hasNext(); ) {
 2060                   Map.Entry e = (Map.Entry) iter.next();
 2061                   Column col = (Column) e.getKey();
 2062                   String val = (String) e.getValue();
 2063                   sql.append(", ").append(col.getName())
 2064                       .append(" = ").append(val);
 2065               }
 2066           }
 2067       }
 2068       
 2069       /**
 2070        * Create SQL to delete the contents of the specified tables. 
 2071        * The default implementation drops all non-deferred RESTRICT foreign key 
 2072        * constraints involving the specified tables, issues DELETE statements 
 2073        * against the tables, and then adds the dropped constraints back in. 
 2074        * Databases with more optimal ways of deleting the contents of several 
 2075        * tables should override this method.
 2076        */
 2077       public String[] getDeleteTableContentsSQL(Table[] tables) {
 2078           Collection sql = new ArrayList();
 2079           
 2080           // collect and drop non-deferred physical restrict constraints, and
 2081           // collect the DELETE FROM statements
 2082           Collection deleteSQL = new ArrayList(tables.length);
 2083           Collection restrictConstraints = new LinkedHashSet();
 2084           for (int i = 0; i < tables.length; i++) {
 2085               ForeignKey[] fks = tables[i].getForeignKeys();
 2086               for (int j = 0; j < fks.length; j++) {
 2087                   if (!fks[j].isLogical() && !fks[j].isDeferred() 
 2088                       && fks[j].getDeleteAction() == ForeignKey.ACTION_RESTRICT)
 2089                   restrictConstraints.add(fks[j]);
 2090                   String[] constraintSQL = getDropForeignKeySQL(fks[j]);
 2091                   sql.addAll(Arrays.asList(constraintSQL));
 2092               }
 2093               
 2094               deleteSQL.add("DELETE FROM " + tables[i].getFullName());
 2095           }
 2096           
 2097           // add the delete statements after all the constraint mutations
 2098           sql.addAll(deleteSQL);
 2099           
 2100           // add the deleted constraints back to the schema
 2101           for (Iterator iter = restrictConstraints.iterator(); iter.hasNext(); ) {
 2102               String[] constraintSQL = 
 2103                   getAddForeignKeySQL((ForeignKey) iter.next());
 2104               sql.addAll(Arrays.asList(constraintSQL));
 2105           }
 2106           
 2107           return (String[]) sql.toArray(new String[sql.size()]);
 2108       }
 2109   
 2110       /**
 2111        * Create a SELECT statement in the proper join syntax for the given
 2112        * instance.
 2113        */
 2114       public SQLBuffer toSelect(Select sel, boolean forUpdate,
 2115           JDBCFetchConfiguration fetch) {
 2116           sel.addJoinClassConditions();
 2117           boolean update = forUpdate && sel.getFromSelect() == null;
 2118           SQLBuffer select = getSelects(sel, false, update);
 2119           SQLBuffer ordering = null;
 2120           if (!sel.isAggregate() || sel.getGrouping() != null)
 2121               ordering = sel.getOrdering();
 2122           SQLBuffer from;
 2123           if (sel.getFromSelect() != null)
 2124               from = getFromSelect(sel, forUpdate);
 2125           else
 2126               from = getFrom(sel, update);
 2127           SQLBuffer where = getWhere(sel, update);
 2128           return toSelect(select, fetch, from, where, sel.getGrouping(),
 2129               sel.getHaving(), ordering, sel.isDistinct(), forUpdate,
 2130               sel.getStartIndex(), sel.getEndIndex(), sel);
 2131       }
 2132   
 2133       /**
 2134        * Return the portion of the select statement between the FROM keyword
 2135        * and the WHERE keyword.
 2136        */
 2137       protected SQLBuffer getFrom(Select sel, boolean forUpdate) {
 2138           SQLBuffer fromSQL = new SQLBuffer(this);
 2139           Collection aliases = sel.getTableAliases();
 2140           if (aliases.size() < 2 || sel.getJoinSyntax() != SYNTAX_SQL92) {
 2141               for (Iterator itr = aliases.iterator(); itr.hasNext();) {
 2142                   fromSQL.append(itr.next().toString());
 2143                   if (forUpdate && tableForUpdateClause != null)
 2144                       fromSQL.append(" ").append(tableForUpdateClause);
 2145                   if (itr.hasNext())
 2146                       fromSQL.append(", ");
 2147               }
 2148           } else {
 2149               Iterator itr = sel.getJoinIterator();
 2150               boolean first = true;
 2151               while (itr.hasNext()) {
 2152                   fromSQL.append(toSQL92Join((Join) itr.next(), forUpdate,
 2153                       first));
 2154                   first = false;
 2155               }
 2156           }
 2157           return fromSQL;
 2158       }
 2159   
 2160       /**
 2161        * Return the FROM clause for a select that selects from a tmp table
 2162        * created by an inner select.
 2163        */
 2164       protected SQLBuffer getFromSelect(Select sel, boolean forUpdate) {
 2165           SQLBuffer fromSQL = new SQLBuffer(this);
 2166           fromSQL.append("(");
 2167           fromSQL.append(toSelect(sel.getFromSelect(), forUpdate, null));
 2168           fromSQL.append(")");
 2169           if (requiresAliasForSubselect)
 2170               fromSQL.append(" ").append(Select.FROM_SELECT_ALIAS);
 2171           return fromSQL;
 2172       }
 2173   
 2174       /**
 2175        * Return the WHERE portion of the select statement, or null if no where
 2176        * conditions.
 2177        */
 2178       protected SQLBuffer getWhere(Select sel, boolean forUpdate) {
 2179           Joins joins = sel.getJoins();
 2180           if (sel.getJoinSyntax() == SYNTAX_SQL92
 2181               || joins == null || joins.isEmpty())
 2182               return sel.getWhere();
 2183   
 2184           SQLBuffer where = new SQLBuffer(this);
 2185           if (sel.getWhere() != null)
 2186               where.append(sel.getWhere());
 2187           if (joins != null)
 2188               sel.append(where, joins);
 2189           return where;
 2190       }
 2191   
 2192       /**
 2193        * Use the given join instance to create SQL joining its tables in
 2194        * the traditional style.
 2195        */
 2196       public SQLBuffer toTraditionalJoin(Join join) {
 2197           ForeignKey fk = join.getForeignKey();
 2198           if (fk == null)
 2199               return null;
 2200   
 2201           boolean inverse = join.isForeignKeyInversed();
 2202           Column[] from = (inverse) ? fk.getPrimaryKeyColumns()
 2203               : fk.getColumns();
 2204           Column[] to = (inverse) ? fk.getColumns()
 2205               : fk.getPrimaryKeyColumns();
 2206   
 2207           // do column joins
 2208           SQLBuffer buf = new SQLBuffer(this);
 2209           int count = 0;
 2210           for (int i = 0; i < from.length; i++, count++) {
 2211               if (count > 0)
 2212                   buf.append(" AND ");
 2213               buf.append(join.getAlias1()).append(".").append(from[i]);
 2214               buf.append(" = ");
 2215               buf.append(join.getAlias2()).append(".").append(to[i]);
 2216           }
 2217   
 2218           // do constant joins
 2219           Column[] constCols = fk.getConstantColumns();
 2220           for (int i = 0; i < constCols.length; i++, count++) {
 2221               if (count > 0)
 2222                   buf.append(" AND ");
 2223               if (inverse)
 2224                   buf.appendValue(fk.getConstant(constCols[i]), constCols[i]);
 2225               else
 2226                   buf.append(join.getAlias1()).append(".").
 2227                       append(constCols[i]);
 2228               buf.append(" = ");
 2229   
 2230               if (inverse)
 2231                   buf.append(join.getAlias2()).append(".").
 2232                       append(constCols[i]);
 2233               else
 2234                   buf.appendValue(fk.getConstant(constCols[i]), constCols[i]);
 2235           }
 2236   
 2237           Column[] constColsPK = fk.getConstantPrimaryKeyColumns();
 2238           for (int i = 0; i < constColsPK.length; i++, count++) {
 2239               if (count > 0)
 2240                   buf.append(" AND ");
 2241               if (inverse)
 2242                   buf.append(join.getAlias1()).append(".").
 2243                       append(constColsPK[i]);
 2244               else
 2245                   buf.appendValue(fk.getPrimaryKeyConstant(constColsPK[i]),
 2246                       constColsPK[i]);
 2247               buf.append(" = ");
 2248   
 2249               if (inverse)
 2250                   buf.appendValue(fk.getPrimaryKeyConstant(constColsPK[i]),
 2251                       constColsPK[i]);
 2252               else
 2253                   buf.append(join.getAlias2()).append(".").
 2254                       append(constColsPK[i]);
 2255           }
 2256           return buf;
 2257       }
 2258   
 2259       /**
 2260        * Use the given join instance to create SQL joining its tables in
 2261        * the SQL92 style.
 2262        */
 2263       public SQLBuffer toSQL92Join(Join join, boolean forUpdate, boolean first) {
 2264           SQLBuffer buf = new SQLBuffer(this);
 2265           if (first) {
 2266               buf.append(join.getTable1()).append(" ").
 2267                   append(join.getAlias1());
 2268               if (forUpdate && tableForUpdateClause != null)
 2269                   buf.append(" ").append(tableForUpdateClause);
 2270           }
 2271   
 2272           buf.append(" ");
 2273           if (join.getType() == Join.TYPE_OUTER)
 2274               buf.append(outerJoinClause);
 2275           else if (join.getType() == Join.TYPE_INNER)
 2276               buf.append(innerJoinClause);
 2277           else // cross
 2278               buf.append(crossJoinClause);
 2279           buf.append(" ");
 2280   
 2281           buf.append(join.getTable2()).append(" ").append(join.getAlias2());
 2282           if (forUpdate && tableForUpdateClause != null)
 2283               buf.append(" ").append(tableForUpdateClause);
 2284   
 2285           if (join.getForeignKey() != null)
 2286               buf.append(" ON ").append(toTraditionalJoin(join));
 2287           else if (requiresConditionForCrossJoin &&
 2288               join.getType() == Join.TYPE_CROSS)
 2289               buf.append(" ON (1 = 1)");
 2290   
 2291           return buf;
 2292       }
 2293   
 2294       /**
 2295        * Use the given join instance to create SQL joining its tables in
 2296        * the database's native syntax. Throws an exception by default.
 2297        */
 2298       public SQLBuffer toNativeJoin(Join join) {
 2299           throw new UnsupportedException();
 2300       }
 2301   
 2302       /**
 2303        * Returns if the given foreign key can be eagerly loaded using other joins.
 2304        */
 2305       public boolean canOuterJoin(int syntax, ForeignKey fk) {
 2306           return syntax != SYNTAX_TRADITIONAL;
 2307       }
 2308   
 2309       /**
 2310        * Combine the given components into a SELECT statement.
 2311        */
 2312       public SQLBuffer toSelect(SQLBuffer selects, JDBCFetchConfiguration fetch,
 2313           SQLBuffer from, SQLBuffer where, SQLBuffer group,
 2314           SQLBuffer having, SQLBuffer order,
 2315           boolean distinct, boolean forUpdate, long start, long end) {
 2316           return toOperation(getSelectOperation(fetch), selects, from, where,
 2317               group, having, order, distinct, start, end,
 2318               getForUpdateClause(fetch, forUpdate, null));
 2319       }
 2320   
 2321       /**
 2322        * Combine the given components into a SELECT statement.
 2323        */
 2324       public SQLBuffer toSelect(SQLBuffer selects, JDBCFetchConfiguration fetch,
 2325           SQLBuffer from, SQLBuffer where, SQLBuffer group,
 2326           SQLBuffer having, SQLBuffer order,
 2327           boolean distinct, boolean forUpdate, long start, long end,
 2328           boolean subselect) {
 2329           return toOperation(getSelectOperation(fetch), selects, from, where,
 2330               group, having, order, distinct, start, end,
 2331               getForUpdateClause(fetch, forUpdate, null), subselect);
 2332       }
 2333       
 2334       public SQLBuffer toSelect(SQLBuffer selects, JDBCFetchConfiguration fetch,
 2335               SQLBuffer from, SQLBuffer where, SQLBuffer group,
 2336               SQLBuffer having, SQLBuffer order,
 2337               boolean distinct, boolean forUpdate, long start, long end,
 2338               boolean subselect, boolean checkTableForUpdate) {
 2339               return toOperation(getSelectOperation(fetch), selects, from, where,
 2340                   group, having, order, distinct, start, end,
 2341                   getForUpdateClause(fetch, forUpdate, null), subselect, checkTableForUpdate);
 2342           }
 2343   
 2344       /**
 2345        * Combine the given components into a SELECT statement.
 2346        */
 2347       public SQLBuffer toSelect(SQLBuffer selects, JDBCFetchConfiguration fetch,
 2348           SQLBuffer from, SQLBuffer where, SQLBuffer group,
 2349           SQLBuffer having, SQLBuffer order,
 2350           boolean distinct, boolean forUpdate, long start, long end,
 2351           Select sel) {
 2352           return toOperation(getSelectOperation(fetch), selects, from, where,
 2353               group, having, order, distinct, start, end,
 2354               getForUpdateClause(fetch, forUpdate, sel));
 2355       }
 2356   
 2357       /**
 2358        * Get the update clause for the query based on the
 2359        * updateClause and isolationLevel hints
 2360        */
 2361       protected String getForUpdateClause(JDBCFetchConfiguration fetch,
 2362           boolean isForUpdate, Select sel) {
 2363           if (fetch != null && fetch.getIsolation() != -1) {
 2364               throw new InvalidStateException(_loc.get(
 2365                   "isolation-level-config-not-supported", getClass().getName()));
 2366           } else if (isForUpdate && !simulateLocking) {
 2367               assertSupport(supportsSelectForUpdate, "SupportsSelectForUpdate");
 2368               return forUpdateClause;
 2369           } else {
 2370               return null;
 2371           }
 2372       }
 2373   
 2374       /**
 2375        * Return the "SELECT" operation clause, adding any available hints, etc.
 2376        */
 2377       public String getSelectOperation(JDBCFetchConfiguration fetch) {
 2378           return "SELECT";
 2379       }
 2380   
 2381       /**
 2382        * Return the SQL for the given selecting operation.
 2383        */
 2384       public SQLBuffer toOperation(String op, SQLBuffer selects,
 2385           SQLBuffer from, SQLBuffer where, SQLBuffer group, SQLBuffer having,
 2386           SQLBuffer order, boolean distinct, long start, long end,
 2387           String forUpdateClause) {
 2388           return toOperation(op, selects, from, where, group, having, order,
 2389               distinct, start, end, forUpdateClause, false);
 2390       }
 2391       
 2392       /**
 2393        * Return the SQL for the given selecting operation.
 2394        */
 2395       public SQLBuffer toOperation(String op, SQLBuffer selects,
 2396           SQLBuffer from, SQLBuffer where, SQLBuffer group, SQLBuffer having,
 2397           SQLBuffer order, boolean distinct, long start, long end,
 2398           String forUpdateClause, boolean subselect) {
 2399           return toOperation(op, selects, from, where, group, having, order,
 2400                   distinct, start, end, forUpdateClause, subselect, false);
 2401       }
 2402   
 2403       /**
 2404        * Return the SQL for the given selecting operation.
 2405        */
 2406       private SQLBuffer toOperation(String op, SQLBuffer selects,
 2407           SQLBuffer from, SQLBuffer where, SQLBuffer group, SQLBuffer having,
 2408           SQLBuffer order, boolean distinct, long start, long end,
 2409           String forUpdateClause, boolean subselect, boolean checkTableForUpdate) {
 2410           SQLBuffer buf = new SQLBuffer(this);
 2411           buf.append(op);
 2412   
 2413           boolean range = start != 0 || end != Long.MAX_VALUE;
 2414           if (range && rangePosition == RANGE_PRE_DISTINCT)
 2415               appendSelectRange(buf, start, end, subselect);
 2416           if (distinct)
 2417               buf.append(" DISTINCT");
 2418           if (range && rangePosition == RANGE_POST_DISTINCT)
 2419               appendSelectRange(buf, start, end, subselect);
 2420   
 2421           buf.append(" ").append(selects).append(" FROM ").append(from);
 2422   
 2423           if (checkTableForUpdate
 2424                   && (StringUtils.isEmpty(forUpdateClause) && !StringUtils
 2425                           .isEmpty(tableForUpdateClause))) {
 2426               buf.append(" ").append(tableForUpdateClause);
 2427           }
 2428   
 2429           if (where != null && !where.isEmpty())
 2430               buf.append(" WHERE ").append(where);
 2431           if (group != null && !group.isEmpty())
 2432               buf.append(" GROUP BY ").append(group);
 2433           if (having != null && !having.isEmpty()) {
 2434               assertSupport(supportsHaving, "SupportsHaving");
 2435               buf.append(" HAVING ").append(having);
 2436           }
 2437           if (order != null && !order.isEmpty())
 2438               buf.append(" ORDER BY ").append(order);
 2439           if (range && rangePosition == RANGE_POST_SELECT)
 2440               appendSelectRange(buf, start, end, subselect);
 2441           if (forUpdateClause != null)
 2442               buf.append(" ").append(forUpdateClause);
 2443           if (range && rangePosition == RANGE_POST_LOCK)
 2444               appendSelectRange(buf, start, end, subselect);
 2445           return buf;
 2446       }
 2447   
 2448       /**
 2449        * If this dictionary can select ranges,
 2450        * use this method to append the range SQL.
 2451        */
 2452       protected void appendSelectRange(SQLBuffer buf, long start, long end,
 2453           boolean subselect) {
 2454       }
 2455   
 2456       /**
 2457        * Return the portion of the select statement between the SELECT keyword
 2458        * and the FROM keyword.
 2459        */
 2460       protected SQLBuffer getSelects(Select sel, boolean distinctIdentifiers,
 2461           boolean forUpdate) {
 2462           // append the aliases for all the columns
 2463           SQLBuffer selectSQL = new SQLBuffer(this);
 2464           List aliases;
 2465           if (distinctIdentifiers)
 2466               aliases = sel.getIdentifierAliases();
 2467           else
 2468               aliases = sel.getSelectAliases();
 2469   
 2470           Object alias;
 2471           for (int i = 0; i < aliases.size(); i++) {
 2472               alias = aliases.get(i);
 2473               appendSelect(selectSQL, alias, sel, i);
 2474               if (i < aliases.size() - 1)
 2475                   selectSQL.append(", ");
 2476           }
 2477           return selectSQL;
 2478       }
 2479   
 2480       /**
 2481        * Append <code>elem</code> to <code>selectSQL</code>.
 2482        * @param selectSQL The SQLBuffer to append to.
 2483        * @param alias A {@link SQLBuffer} or a {@link String} to append.
 2484        *
 2485        * @since 1.1.0
 2486        */
 2487       protected void appendSelect(SQLBuffer selectSQL, Object elem, Select sel,
 2488           int idx) {
 2489           if (elem instanceof SQLBuffer)
 2490               selectSQL.append((SQLBuffer) elem);
 2491           else
 2492               selectSQL.append(elem.toString());
 2493       }
 2494   
 2495       /**
 2496        * Returns true if a "FOR UPDATE" clause can be used for the specified
 2497        * Select object.
 2498        */
 2499       public boolean supportsLocking(Select sel) {
 2500           if (sel.isAggregate())
 2501               return false;
 2502           if (!supportsSelectForUpdate)
 2503               return false;
 2504           if (!supportsLockingWithSelectRange && (sel.getStartIndex() != 0
 2505               || sel.getEndIndex() != Long.MAX_VALUE))
 2506               return false;
 2507   
 2508           // only inner select is locked
 2509           if (sel.getFromSelect() != null)
 2510               sel = sel.getFromSelect();
 2511   
 2512           if (!supportsLockingWithDistinctClause && sel.isDistinct())
 2513               return false;
 2514           if (!supportsLockingWithMultipleTables
 2515               && sel.getTableAliases().size() > 1)
 2516               return false;
 2517           if (!supportsLockingWithOrderClause && sel.getOrdering() != null)
 2518               return false;
 2519           if (!supportsLockingWithOuterJoin || !supportsLockingWithInnerJoin) {
 2520               for (Iterator itr = sel.getJoinIterator(); itr.hasNext();) {
 2521                   Join join = (Join) itr.next();
 2522                   if (!supportsLockingWithOuterJoin
 2523                       && join.getType() == Join.TYPE_OUTER)
 2524                       return false;
 2525                   if (!supportsLockingWithInnerJoin
 2526                       && join.getType() == Join.TYPE_INNER)
 2527                       return false;
 2528               }
 2529           }
 2530           return true;
 2531       }
 2532   
 2533       /**
 2534        * Return false if the given select requires a forward-only result set.
 2535        */
 2536       public boolean supportsRandomAccessResultSet(Select sel,
 2537           boolean forUpdate) {
 2538           return !sel.isAggregate();
 2539       }
 2540   
 2541       /**
 2542        * Assert that the given dictionary flag is true. If it is not true,
 2543        * throw an error saying that the given setting needs to return true for
 2544        * the current operation to work.
 2545        */
 2546       public void assertSupport(boolean feature, String property) {
 2547           if (!feature)
 2548               throw new UnsupportedException(_loc.get("feature-not-supported",
 2549                   getClass(), property));
 2550       }
 2551   
 2552       ////////////////////
 2553       // Query functions
 2554       ////////////////////
 2555   
 2556       /**
 2557        * Invoke this database's substring function.
 2558        *
 2559        * @param buf the SQL buffer to write the substring invocation to
 2560        * @param str a query value representing the target string
 2561        * @param start a query value representing the start index
 2562        * @param end a query value representing the end index, or null for none
 2563        */
 2564       public void substring(SQLBuffer buf, FilterValue str, FilterValue start,
 2565           FilterValue end) {
 2566           buf.append(substringFunctionName).append("(");
 2567           str.appendTo(buf);
 2568           buf.append(", ");
 2569           if (start.getValue() instanceof Number) {
 2570               long startLong = toLong(start);
 2571               buf.append(Long.toString(startLong + 1));
 2572           } else {
 2573               buf.append("(");
 2574               start.appendTo(buf);
 2575               buf.append(" + 1)");
 2576           }
 2577           if (end != null) {
 2578               buf.append(", ");
 2579               if (start.getValue() instanceof Number
 2580                   && end.getValue() instanceof Number) {
 2581                   long startLong = toLong(start);
 2582                   long endLong = toLong(end);
 2583                   buf.append(Long.toString(endLong - startLong));
 2584               } else {
 2585                   end.appendTo(buf);
 2586                   buf.append(" - (");
 2587                   start.appendTo(buf);
 2588                   buf.append(")");
 2589               }
 2590           }
 2591           buf.append(")");
 2592       }
 2593   
 2594       long toLong(FilterValue litValue) {
 2595           return ((Number) litValue.getValue()).longValue();
 2596       }
 2597   
 2598       /**
 2599        * Invoke this database's indexOf function.
 2600        *
 2601        * @param buf the SQL buffer to write the indexOf invocation to
 2602        * @param str a query value representing the target string
 2603        * @param find a query value representing the search string
 2604        * @param start a query value representing the start index, or null
 2605        * to start at the beginning
 2606        */
 2607       public void indexOf(SQLBuffer buf, FilterValue str, FilterValue find,
 2608           FilterValue start) {
 2609           buf.append("(INSTR((");
 2610           if (start != null)
 2611               substring(buf, str, start, null);
 2612           else
 2613               str.appendTo(buf);
 2614           buf.append("), (");
 2615           find.appendTo(buf);
 2616           buf.append(")) - 1");
 2617           if (start != null) {
 2618               buf.append(" + ");
 2619               start.appendTo(buf);
 2620           }
 2621           buf.append(")");
 2622       }
 2623   
 2624       /**
 2625        * Append the numeric parts of a mathematical function.
 2626        *
 2627        * @param buf the SQL buffer to write the math function
 2628        * @param op the mathematical operation to perform
 2629        * @param lhs the left hand side of the math function
 2630        * @param rhs the right hand side of the math function
 2631        */
 2632       public void mathFunction(SQLBuffer buf, String op, FilterValue lhs,
 2633           FilterValue rhs) {
 2634           boolean castlhs = false;
 2635           boolean castrhs = false;
 2636           Class lc = Filters.wrap(lhs.getType());
 2637           Class rc = Filters.wrap(rhs.getType());
 2638           int type = 0;
 2639           if (requiresCastForMathFunctions && (lc != rc
 2640               || (lhs.isConstant() && rhs.isConstant()))) {
 2641               Class c = Filters.promote(lc, rc);
 2642               type = getJDBCType(JavaTypes.getTypeCode(c), false);
 2643               if (type != Types.VARBINARY && type != Types.BLOB) {
 2644                   castlhs = (lhs.isConstant() && rhs.isConstant()) || lc != c;
 2645                   castrhs = (lhs.isConstant() && rhs.isConstant()) || rc != c;
 2646               }
 2647           }
 2648   
 2649           boolean mod = "MOD".equals(op);
 2650           if (mod) {
 2651               if (supportsModOperator)
 2652                   op = "%";
 2653               else
 2654                   buf.append(op);
 2655           }
 2656           buf.append("(");
 2657   
 2658           if (castlhs)
 2659               appendCast(buf, lhs, type);
 2660           else
 2661               lhs.appendTo(buf);
 2662   
 2663           if (mod && !supportsModOperator)
 2664               buf.append(", ");
 2665           else
 2666               buf.append(" ").append(op).append(" ");
 2667   
 2668           if (castrhs)
 2669               appendCast(buf, rhs, type);
 2670           else
 2671               rhs.appendTo(buf);
 2672   
 2673           buf.append(")");
 2674       }
 2675   
 2676       /**
 2677        * Append a comparison.
 2678        *
 2679        * @param buf the SQL buffer to write the comparison
 2680        * @param op the comparison operation to perform
 2681        * @param lhs the left hand side of the comparison
 2682        * @param rhs the right hand side of the comparison
 2683        */
 2684       public void comparison(SQLBuffer buf, String op, FilterValue lhs,
 2685           FilterValue rhs) {
 2686           boolean lhsxml = lhs.getXPath() != null;
 2687           boolean rhsxml = rhs.getXPath() != null;
 2688           if (lhsxml || rhsxml) {
 2689               appendXmlComparison(buf, op, lhs, rhs, lhsxml, rhsxml);
 2690               return;
 2691           }
 2692           boolean castlhs = false;
 2693           boolean castrhs = false;
 2694           Class lc = Filters.wrap(lhs.getType());
 2695           Class rc = Filters.wrap(rhs.getType());
 2696           int type = 0;
 2697           if (requiresCastForComparisons && (lc != rc
 2698               || (lhs.isConstant() && rhs.isConstant()))) {
 2699               Class c = Filters.promote(lc, rc);
 2700               type = getJDBCType(JavaTypes.getTypeCode(c), false);
 2701               if (type != Types.VARBINARY && type != Types.BLOB) {
 2702                   castlhs = (lhs.isConstant() && rhs.isConstant()) || lc != c;
 2703                   castrhs = (lhs.isConstant() && rhs.isConstant()) || rc != c;
 2704               }
 2705           }
 2706   
 2707           if (castlhs)
 2708               appendCast(buf, lhs, type);
 2709           else
 2710               lhs.appendTo(buf);
 2711   
 2712           buf.append(" ").append(op).append(" ");
 2713   
 2714           if (castrhs)
 2715               appendCast(buf, rhs, type);
 2716           else
 2717               rhs.appendTo(buf);
 2718       }
 2719   
 2720       /**
 2721        * If this dictionary supports XML type,
 2722        * use this method to append xml predicate.
 2723        */
 2724       public void appendXmlComparison(SQLBuffer buf, String op, FilterValue lhs,
 2725           FilterValue rhs, boolean lhsxml, boolean rhsxml) {
 2726           assertSupport(supportsXMLColumn, "SupportsXMLColumn");
 2727       }
 2728   
 2729       /**
 2730        * Append SQL for the given numeric value to the buffer, casting as needed.
 2731        */
 2732       protected void appendNumericCast(SQLBuffer buf, FilterValue val) {
 2733           if (val.isConstant())
 2734               appendCast(buf, val, Types.NUMERIC);
 2735           else
 2736               val.appendTo(buf);
 2737       }
 2738   
 2739       /**
 2740        * Cast the specified value to the specified type.
 2741        *
 2742        * @param buf the buffer to append the cast to
 2743        * @param val the value to cast
 2744        * @param type the type of the case, e.g. {@link Types#NUMERIC}
 2745        */
 2746       public void appendCast(SQLBuffer buf, Object val, int type) {
 2747           // Convert the cast function: "CAST({0} AS {1})"
 2748           int firstParam = castFunction.indexOf("{0}");
 2749           String pre = castFunction.substring(0, firstParam); // "CAST("
 2750           String mid = castFunction.substring(firstParam + 3);
 2751           int secondParam = mid.indexOf("{1}");
 2752           String post;
 2753           if (secondParam > -1) {
 2754               post = mid.substring(secondParam + 3); // ")"
 2755               mid = mid.substring(0, secondParam); // " AS "
 2756           } else
 2757               post = "";
 2758   
 2759           buf.append(pre);
 2760           if (val instanceof FilterValue)
 2761               ((FilterValue) val).appendTo(buf);
 2762           else if (val instanceof SQLBuffer)
 2763               buf.append(((SQLBuffer) val));
 2764           else
 2765               buf.append(val.toString());
 2766           buf.append(mid);
 2767           buf.append(getTypeName(type));
 2768           appendLength(buf, type);
 2769           buf.append(post);
 2770       }
 2771       
 2772       protected void appendLength(SQLBuffer buf, int type) {        
 2773       }
 2774   
 2775       
 2776       /**
 2777        * add CAST for a function operator where operand is a param
 2778        * @param func  function name
 2779        * @param val 
 2780        * @return updated func
 2781        */
 2782       public String addCastAsType(String func, Val val) {
 2783           return null;
 2784       }    
 2785   
 2786   
 2787       ///////////
 2788       // DDL SQL
 2789       ///////////
 2790   
 2791       /**
 2792        * Increment the reference count of any table components that this
 2793        * dictionary adds that are not used by mappings. Does nothing by default.
 2794        */
 2795       public void refSchemaComponents(Table table) {
 2796       }
 2797   
 2798       /**
 2799        * Returns the full name of the table, including the schema (delimited
 2800        * by {@link #catalogSeparator}).
 2801        */
 2802       public String getFullName(Table table, boolean logical) {
 2803           if (!useSchemaName || table.getSchemaName() == null)
 2804               return table.getName();
 2805           if (logical || ".".equals(catalogSeparator))
 2806               return table.getFullName();
 2807           return table.getSchemaName() + catalogSeparator + table.getName();
 2808       }
 2809   
 2810       /**
 2811        * Returns the full name of the index, including the schema (delimited
 2812        * by the result of {@link #catalogSeparator}).
 2813        */
 2814       public String getFullName(Index index) {
 2815           if (!useSchemaName || index.getSchemaName() == null)
 2816               return index.getName();
 2817           if (".".equals(catalogSeparator))
 2818               return index.getFullName();
 2819           return index.getSchemaName() + catalogSeparator + index.getName();
 2820       }
 2821   
 2822       /**
 2823        * Returns the full name of the sequence, including the schema (delimited
 2824        * by the result of {@link #catalogSeparator}).
 2825        */
 2826       public String getFullName(Sequence seq) {
 2827           if (!useSchemaName || seq.getSchemaName() == null)
 2828               return seq.getName();
 2829           if (".".equals(catalogSeparator))
 2830               return seq.getFullName();
 2831           return seq.getSchemaName() + catalogSeparator + seq.getName();
 2832       }
 2833   
 2834       /**
 2835        * Make any necessary changes to the given table name to make it valid
 2836        * for the current DB.
 2837        */
 2838       public String getValidTableName(String name, Schema schema) {
 2839           while (name.startsWith("_"))
 2840               name = name.substring(1);
 2841           return makeNameValid(name, schema.getSchemaGroup(),
 2842               maxTableNameLength, NAME_TABLE);
 2843       }
 2844   
 2845       /**
 2846        * Make any necessary changes to the given sequence name to make it valid
 2847        * for the current DB.
 2848        */
 2849       public String getValidSequenceName(String name, Schema schema) {
 2850           while (name.startsWith("_"))
 2851               name = name.substring(1);
 2852           return makeNameValid("S_" + name, schema.getSchemaGroup(),
 2853               maxTableNameLength, NAME_SEQUENCE);
 2854       }
 2855   
 2856       /**
 2857        * Make any necessary changes to the given column name to make it valid
 2858        * for the current DB.  The column name will be made unique for the
 2859        * specified table.
 2860        */
 2861       public String getValidColumnName(String name, Table table) {
 2862           return getValidColumnName(name, table, true);
 2863       }
 2864   
 2865       /**
 2866        * Make any necessary changes to the given column name to make it valid
 2867        * for the current DB.  If checkForUniqueness is true, the column name will 
 2868        * be made unique for the specified table.
 2869        */
 2870       public String getValidColumnName(String name, Table table,
 2871           boolean checkForUniqueness) {
 2872           while (name.startsWith("_"))
 2873               name = name.substring(1);
 2874           return makeNameValid(name, table, maxColumnNameLength, NAME_ANY,
 2875               checkForUniqueness);
 2876       }
 2877   
 2878       /**
 2879        * Make any necessary changes to the given primary key name to make it
 2880        * valid for the current DB.
 2881        */
 2882       public String getValidPrimaryKeyName(String name, Table table) {
 2883           while (name.startsWith("_"))
 2884               name = name.substring(1);
 2885           return makeNameValid("P_" + name, table.getSchema().getSchemaGroup(),
 2886               maxConstraintNameLength, NAME_ANY);
 2887       }
 2888   
 2889       /**
 2890        * Make any necessary changes to the given foreign key name to make it
 2891        * valid for the current DB.
 2892        */
 2893       public String getValidForeignKeyName(String name, Table table,
 2894           Table toTable) {
 2895           while (name.startsWith("_"))
 2896               name = name.substring(1);
 2897           String tableName = table.getName();
 2898           int len = Math.min(tableName.length(), 7);
 2899           name = "F_" + shorten(tableName, len) + "_" + name;
 2900           return makeNameValid(name, table.getSchema().getSchemaGroup(),
 2901               maxConstraintNameLength, NAME_ANY);
 2902       }
 2903   
 2904       /**
 2905        * Make any necessary changes to the given index name to make it valid
 2906        * for the current DB.
 2907        */
 2908       public String getValidIndexName(String name, Table table) {
 2909           while (name.startsWith("_"))
 2910               name = name.substring(1);
 2911           String tableName = table.getName();
 2912           int len = Math.min(tableName.length(), 7);
 2913           name = "I_" + shorten(tableName, len) + "_" + name;
 2914           return makeNameValid(name, table.getSchema().getSchemaGroup(),
 2915               maxIndexNameLength, NAME_ANY);
 2916       }
 2917   
 2918       /**
 2919        * Make any necessary changes to the given unique constraint name to make
 2920        * it valid for the current DB.
 2921        */
 2922       public String getValidUniqueName(String name, Table table) {
 2923           while (name.startsWith("_"))
 2924               name = name.substring(1);
 2925           String tableName = table.getName();
 2926           int len = Math.min(tableName.length(), 7);
 2927           name = "U_" + shorten(tableName, len) + "_" + name;
 2928           return makeNameValid(name, table.getSchema().getSchemaGroup(),
 2929               maxConstraintNameLength, NAME_ANY);
 2930       }
 2931   
 2932       /**
 2933        * Shorten the specified name to the specified target name. This will
 2934        * be done by first stripping out the vowels, and then removing
 2935        * characters from the middle of the word until it reaches the target
 2936        * length.
 2937        */
 2938       protected static String shorten(String name, int targetLength) {
 2939           if (name == null || name.length() <= targetLength)
 2940               return name;
 2941   
 2942           StringBuffer nm = new StringBuffer(name);
 2943           while (nm.length() > targetLength) {
 2944               if (!stripVowel(nm)) {
 2945                   // cut out the middle char
 2946                   nm.replace(nm.length() / 2, (nm.length() / 2) + 1, "");
 2947               }
 2948           }
 2949           return nm.toString();
 2950       }
 2951   
 2952       /**
 2953        * Remove vowels from the specified StringBuffer.
 2954        *
 2955        * @return true if any vowels have been removed
 2956        */
 2957       private static boolean stripVowel(StringBuffer name) {
 2958           if (name == null || name.length() == 0)
 2959               return false;
 2960   
 2961           char[] vowels = { 'A', 'E', 'I', 'O', 'U', };
 2962           for (int i = 0; i < vowels.length; i++) {
 2963               int index = name.toString().toUpperCase().indexOf(vowels[i]);
 2964               if (index != -1) {
 2965                   name.replace(index, index + 1, "");
 2966                   return true;
 2967               }
 2968           }
 2969           return false;
 2970       }
 2971   
 2972       /**
 2973        * Shortens the given name to the given maximum length, then checks that
 2974        * it is not a reserved word. If it is reserved, appends a "0". If
 2975        * the name conflicts with an existing schema component, the last
 2976        * character is replace with '0', then '1', etc.
 2977        * Note that the given max len may be 0 if the database metadata is
 2978        * incomplete.
 2979        */
 2980       protected String makeNameValid(String name, NameSet set, int maxLen,
 2981           int nameType) {
 2982           return makeNameValid(name, set, maxLen, nameType, true);
 2983       }
 2984   
 2985       /**
 2986        * Shortens the given name to the given maximum length, then checks that
 2987        * it is not a reserved word. If it is reserved, appends a "0". If
 2988        * the name conflicts with an existing schema component and uniqueness
 2989        * checking is enabled, the last character is replace with '0', then 
 2990        * '1', etc. 
 2991        * Note that the given max len may be 0 if the database metadata is 
 2992        * incomplete.
 2993        */
 2994       protected String makeNameValid(String name, NameSet set, int maxLen,
 2995           int nameType, boolean checkForUniqueness) {
 2996           if (maxLen < 1)
 2997               maxLen = 255;
 2998           if (name.length() > maxLen)
 2999               name = name.substring(0, maxLen);
 3000           if (reservedWordSet.contains(name.toUpperCase())) {
 3001               if (name.length() == maxLen)
 3002                   name = name.substring(0, name.length() - 1);
 3003               name += "0";
 3004           }
 3005   
 3006           // now make sure the name is unique
 3007           if (set != null && checkForUniqueness) {
 3008               outer:
 3009               for (int version = 1, chars = 1; true; version++) {
 3010                   // for table names, we check for the table itself in case the
 3011                   // name set is lazy about schema reflection
 3012                   switch (nameType) {
 3013                       case NAME_TABLE:
 3014                           if (!((SchemaGroup) set).isKnownTable(name))
 3015                               break outer;
 3016                           break;
 3017                       case NAME_SEQUENCE:
 3018                           if (!((SchemaGroup) set).isKnownSequence(name))
 3019                               break outer;
 3020                           break;
 3021                       default:
 3022                           if (!set.isNameTaken(name))
 3023                               break outer;
 3024                   }
 3025   
 3026                   // a single char for the version is probably enough, but might
 3027                   // as well be general about it...
 3028                   if (version > 1)
 3029                       name = name.substring(0, name.length() - chars);
 3030                   if (version >= Math.pow(10, chars))
 3031                       chars++;
 3032                   if (name.length() + chars > maxLen)
 3033                       name = name.substring(0, maxLen - chars);
 3034                   name = name + version;
 3035               }
 3036           }
 3037           return name.toUpperCase();
 3038       }
 3039   
 3040       /**
 3041        * Return a series of SQL statements to create the given table, complete
 3042        * with columns. Indexes and constraints will be created separately.
 3043        */
 3044       public String[] getCreateTableSQL(Table table) {
 3045           StringBuffer buf = new StringBuffer();
 3046           buf.append("CREATE TABLE ").append(getFullName(table, false));
 3047           if (supportsComments && table.hasComment()) {
 3048               buf.append(" ");
 3049               comment(buf, table.getComment());
 3050               buf.append("\n    (");
 3051           } else {
 3052               buf.append(" (");
 3053           }
 3054   
 3055           // do this before getting the columns so we know how to handle
 3056           // the last comma
 3057           StringBuffer endBuf = new StringBuffer();
 3058           PrimaryKey pk = table.getPrimaryKey();
 3059           String pkStr;
 3060           if (pk != null) {
 3061               pkStr = getPrimaryKeyConstraintSQL(pk);
 3062               if (pkStr != null)
 3063                   endBuf.append(pkStr);
 3064           }
 3065   
 3066           Unique[] unqs = table.getUniques();
 3067           String unqStr;
 3068           for (int i = 0; i < unqs.length; i++) {
 3069               unqStr = getUniqueConstraintSQL(unqs[i]);
 3070               if (unqStr != null) {
 3071                   if (endBuf.length() > 0)
 3072                       endBuf.append(", ");
 3073                   endBuf.append(unqStr);
 3074               }
 3075           }
 3076   
 3077           Column[] cols = table.getColumns();
 3078           for (int i = 0; i < cols.length; i++) {
 3079               buf.append(getDeclareColumnSQL(cols[i], false));
 3080               if (i < cols.length - 1 || endBuf.length() > 0)
 3081                   buf.append(", ");
 3082               if (supportsComments && cols[i].hasComment()) {
 3083                   comment(buf, cols[i].getComment());
 3084                   buf.append("\n    ");
 3085               }
 3086           }
 3087   
 3088           buf.append(endBuf.toString());
 3089           buf.append(")");
 3090           return new String[]{ buf.toString() };
 3091       }
 3092   
 3093       protected StringBuffer comment(StringBuffer buf, String comment) {
 3094           return buf.append("-- ").append(comment);
 3095       }
 3096   
 3097       /**
 3098        * Return a series of SQL statements to drop the given table. Indexes
 3099        * will be dropped separately. Returns
 3100        * <code>DROP TABLE &lt;table name&gt;</code> by default.
 3101        */
 3102       public String[] getDropTableSQL(Table table) {
 3103           String drop = MessageFormat.format(dropTableSQL, new Object[]{
 3104               getFullName(table, false) });
 3105           return new String[]{ drop };
 3106       }
 3107   
 3108       /**
 3109        * Return a series of SQL statements to create the given sequence. Returns
 3110        * <code>CREATE SEQUENCE &lt;sequence name&gt;[ START WITH &lt;start&gt;]
 3111        * [ INCREMENT BY &lt;increment&gt;]</code> by default.
 3112        */
 3113       public String[] getCreateSequenceSQL(Sequence seq) {
 3114           if (nextSequenceQuery == null)
 3115               return null;
 3116   
 3117           StringBuffer buf = new StringBuffer();
 3118           buf.append("CREATE SEQUENCE ");
 3119           buf.append(getFullName(seq));
 3120           if (seq.getInitialValue() != 0)
 3121               buf.append(" START WITH ").append(seq.getInitialValue());
 3122           if (seq.getIncrement() > 1)
 3123               buf.append(" INCREMENT BY ").append(seq.getIncrement());
 3124           return new String[]{ buf.toString() };
 3125       }
 3126   
 3127       /**
 3128        * Return a series of SQL statements to drop the given sequence. Returns
 3129        * <code>DROP SEQUENCE &lt;sequence name&gt;</code> by default.
 3130        */
 3131       public String[] getDropSequenceSQL(Sequence seq) {
 3132           return new String[]{ "DROP SEQUENCE " + getFullName(seq) };
 3133       }
 3134   
 3135       /**
 3136        * Return a series of SQL statements to create the given index. Returns
 3137        * <code>CREATE [UNIQUE] INDEX &lt;index name&gt; ON &lt;table name&gt;
 3138        * (&lt;col list&gt;)</code> by default.
 3139        */
 3140       public String[] getCreateIndexSQL(Index index) {
 3141           StringBuffer buf = new StringBuffer();
 3142           buf.append("CREATE ");
 3143           if (index.isUnique())
 3144               buf.append("UNIQUE ");
 3145           buf.append("INDEX ").append(index.getName());
 3146   
 3147           buf.append(" ON ").append(getFullName(index.getTable(), false));
 3148           buf.append(" (").append(Strings.join(index.getColumns(), ", ")).
 3149               append(")");
 3150   
 3151           return new String[]{ buf.toString() };
 3152       }
 3153   
 3154       /**
 3155        * Return a series of SQL statements to drop the given index. Returns
 3156        * <code>DROP INDEX &lt;index name&gt;</code> by default.
 3157        */
 3158       public String[] getDropIndexSQL(Index index) {
 3159           return new String[]{ "DROP INDEX " + getFullName(index) };
 3160       }
 3161   
 3162       /**
 3163        * Return a series of SQL statements to add the given column to
 3164        * its table. Return an empty array if operation not supported. Returns
 3165        * <code>ALTER TABLE &lt;table name&gt; ADD (&lt;col dec&gt;)</code>
 3166        * by default.
 3167        */
 3168       public String[] getAddColumnSQL(Column column) {
 3169           if (!supportsAlterTableWithAddColumn)
 3170               return new String[0];
 3171   
 3172           String dec = getDeclareColumnSQL(column, true);
 3173           if (dec == null)
 3174               return new String[0];
 3175           return new String[]{ "ALTER TABLE "
 3176               + getFullName(column.getTable(), false) + " ADD " + dec };
 3177       }
 3178   
 3179       /**
 3180        * Return a series of SQL statements to drop the given column from
 3181        * its table. Return an empty array if operation not supported. Returns
 3182        * <code>ALTER TABLE &lt;table name&gt; DROP COLUMN &lt;col name&gt;</code>
 3183        * by default.
 3184        */
 3185       public String[] getDropColumnSQL(Column column) {
 3186           if (!supportsAlterTableWithDropColumn)
 3187               return new String[0];
 3188           return new String[]{ "ALTER TABLE "
 3189               + getFullName(column.getTable(), false)
 3190               + " DROP COLUMN " + column };
 3191       }
 3192   
 3193       /**
 3194        * Return a series of SQL statements to add the given primary key to
 3195        * its table. Return an empty array if operation not supported.
 3196        * Returns <code>ALTER TABLE &lt;table name&gt; ADD
 3197        * &lt;pk cons sql &gt;</code> by default.
 3198        */
 3199       public String[] getAddPrimaryKeySQL(PrimaryKey pk) {
 3200           String pksql = getPrimaryKeyConstraintSQL(pk);
 3201           if (pksql == null)
 3202               return new String[0];
 3203           return new String[]{ "ALTER TABLE "
 3204               + getFullName(pk.getTable(), false) + " ADD " + pksql };
 3205       }
 3206   
 3207       /**
 3208        * Return a series of SQL statements to drop the given primary key from
 3209        * its table. Return an empty array if operation not supported.
 3210        * Returns <code>ALTER TABLE &lt;table name&gt; DROP CONSTRAINT
 3211        * &lt;pk name&gt;</code> by default.
 3212        */
 3213       public String[] getDropPrimaryKeySQL(PrimaryKey pk) {
 3214           if (pk.getName() == null)
 3215               return new String[0];
 3216           return new String[]{ "ALTER TABLE "
 3217               + getFullName(pk.getTable(), false)
 3218               + " DROP CONSTRAINT " + pk.getName() };
 3219       }
 3220   
 3221       /**
 3222        * Return a series of SQL statements to add the given foreign key to
 3223        * its table. Return an empty array if operation not supported.
 3224        * Returns <code>ALTER TABLE &lt;table name&gt; ADD
 3225        * &lt;fk cons sql &gt;</code> by default.
 3226        */
 3227       public String[] getAddForeignKeySQL(ForeignKey fk) {
 3228           String fkSQL = getForeignKeyConstraintSQL(fk);
 3229           if (fkSQL == null)
 3230               return new String[0];
 3231           return new String[]{ "ALTER TABLE "
 3232               + getFullName(fk.getTable(), false) + " ADD " + fkSQL };
 3233       }
 3234   
 3235       /**
 3236        * Return a series of SQL statements to drop the given foreign key from
 3237        * its table. Return an empty array if operation not supported.
 3238        * Returns <code>ALTER TABLE &lt;table name&gt; DROP CONSTRAINT
 3239        * &lt;fk name&gt;</code> by default.
 3240        */
 3241       public String[] getDropForeignKeySQL(ForeignKey fk) {
 3242           if (fk.getName() == null)
 3243               return new String[0];
 3244           return new String[]{ "ALTER TABLE "
 3245               + getFullName(fk.getTable(), false)
 3246               + " DROP CONSTRAINT " + fk.getName() };
 3247       }
 3248   
 3249       /**
 3250        * Return the declaration SQL for the given column. This method is used
 3251        * for each column from within {@link #getCreateTableSQL} and
 3252        * {@link #getAddColumnSQL}.
 3253        */
 3254       protected String getDeclareColumnSQL(Column col, boolean alter) {
 3255           StringBuffer buf = new StringBuffer();
 3256           buf.append(col).append(" ");
 3257           buf.append(getTypeName(col));
 3258   
 3259           // can't add constraints to a column we're adding after table
 3260           // creation, cause some data might already be inserted
 3261           if (!alter) {
 3262               if (col.getDefaultString() != null && !col.isAutoAssigned())
 3263                   buf.append(" DEFAULT ").append(col.getDefaultString());
 3264               if (col.isNotNull())
 3265                   buf.append(" NOT NULL");
 3266           }
 3267           if (col.isAutoAssigned()) {
 3268               if (!supportsAutoAssign)
 3269                   log.warn(_loc.get("invalid-autoassign", platform, col));
 3270               else if (autoAssignClause != null)
 3271                   buf.append(" ").append(autoAssignClause);
 3272           }
 3273           return buf.toString();
 3274       }
 3275   
 3276       /**
 3277        * Return the declaration SQL for the given primary key. This method is
 3278        * used from within {@link #getCreateTableSQL} and
 3279        * {@link #getAddPrimaryKeySQL}. Returns
 3280        * <code>CONSTRAINT &lt;pk name&gt; PRIMARY KEY (&lt;col list&gt;)</code>
 3281        * by default.
 3282        */
 3283       protected String getPrimaryKeyConstraintSQL(PrimaryKey pk) {
 3284           // if we have disabled the creation of primary keys, abort here
 3285           if (!createPrimaryKeys)
 3286               return null;
 3287   
 3288           String name = pk.getName();
 3289           if (name != null && reservedWordSet.contains(name.toUpperCase()))
 3290               name = null;
 3291   
 3292           StringBuffer buf = new StringBuffer();
 3293           if (name != null && CONS_NAME_BEFORE.equals(constraintNameMode))
 3294               buf.append("CONSTRAINT ").append(name).append(" ");
 3295           buf.append("PRIMARY KEY ");
 3296           if (name != null && CONS_NAME_MID.equals(constraintNameMode))
 3297               buf.append(name).append(" ");
 3298           buf.append("(").append(Strings.join(pk.getColumns(), ", ")).
 3299               append(")");
 3300           if (name != null && CONS_NAME_AFTER.equals(constraintNameMode))
 3301               buf.append(" CONSTRAINT ").append(name);
 3302           return buf.toString();
 3303       }
 3304   
 3305       /**
 3306        * Return the declaration SQL for the given foreign key, or null if it is
 3307        * not supported. This method is used from within
 3308        * {@link #getCreateTableSQL} and {@link #getAddForeignKeySQL}. Returns
 3309        * <code>CONSTRAINT &lt;cons name&gt; FOREIGN KEY (&lt;col list&gt;)
 3310        * REFERENCES &lt;foreign table&gt; (&lt;col list&gt;)
 3311        * [ON DELETE &lt;action&gt;] [ON UPDATE &lt;action&gt;]</code> by default.
 3312        */
 3313       protected String getForeignKeyConstraintSQL(ForeignKey fk) {
 3314           if (!supportsForeignKeys)
 3315               return null;
 3316           if (fk.getDeleteAction() == ForeignKey.ACTION_NONE)
 3317               return null;
 3318           if (fk.isDeferred() && !supportsDeferredForeignKeyConstraints())
 3319               return null;
 3320           if (!supportsDeleteAction(fk.getDeleteAction())
 3321               || !supportsUpdateAction(fk.getUpdateAction()))
 3322               return null;
 3323   
 3324           Column[] locals = fk.getColumns();
 3325           Column[] foreigns = fk.getPrimaryKeyColumns();
 3326   
 3327           int delActionId = fk.getDeleteAction();
 3328           if (delActionId == ForeignKey.ACTION_NULL) {
 3329               for (int i = 0; i < locals.length; i++) {
 3330                   if (locals[i].isNotNull())
 3331                       delActionId = ForeignKey.ACTION_NONE;
 3332               }
 3333           }
 3334   
 3335           String delAction = getActionName(delActionId);
 3336           String upAction = getActionName(fk.getUpdateAction());
 3337   
 3338           StringBuffer buf = new StringBuffer();
 3339           if (fk.getName() != null
 3340               && CONS_NAME_BEFORE.equals(constraintNameMode))
 3341               buf.append("CONSTRAINT ").append(fk.getName()).append(" ");
 3342           buf.append("FOREIGN KEY ");
 3343           if (fk.getName() != null && CONS_NAME_MID.equals(constraintNameMode))
 3344               buf.append(fk.getName()).append(" ");
 3345           buf.append("(").append(Strings.join(locals, ", ")).append(")");
 3346           buf.append(" REFERENCES ");
 3347           buf.append(getFullName(foreigns[0].getTable(), false));
 3348           buf.append(" (").append(Strings.join(foreigns, ", ")).append(")");
 3349           if (delAction != null)
 3350               buf.append(" ON DELETE ").append(delAction);
 3351           if (upAction != null)
 3352               buf.append(" ON UPDATE ").append(upAction);
 3353           if (fk.isDeferred())
 3354               buf.append(" INITIALLY DEFERRED");
 3355           if (supportsDeferredForeignKeyConstraints())
 3356               buf.append(" DEFERRABLE");
 3357           if (fk.getName() != null
 3358               && CONS_NAME_AFTER.equals(constraintNameMode))
 3359               buf.append(" CONSTRAINT ").append(fk.getName());
 3360           return buf.toString();
 3361       }
 3362   
 3363       /**
 3364        * Whether or not this dictionary supports deferred foreign key constraints.
 3365        * This implementation returns {@link #supportsUniqueConstraints}.
 3366        *
 3367        * @since 1.1.0
 3368        */
 3369       protected boolean supportsDeferredForeignKeyConstraints() {
 3370           return supportsDeferredConstraints;
 3371       }
 3372   
 3373       /**
 3374        * Return the name of the given foreign key action.
 3375        */
 3376       private String getActionName(int action) {
 3377           switch (action) {
 3378               case ForeignKey.ACTION_CASCADE:
 3379                   return "CASCADE";
 3380               case ForeignKey.ACTION_NULL:
 3381                   return "SET NULL";
 3382               case ForeignKey.ACTION_DEFAULT:
 3383                   return "SET DEFAULT";
 3384               default:
 3385                   return null;
 3386           }
 3387       }
 3388   
 3389       /**
 3390        * Whether this database supports the given foreign key delete action.
 3391        */
 3392       public boolean supportsDeleteAction(int action) {
 3393           if (action == ForeignKey.ACTION_NONE)
 3394               return true;
 3395           if (!supportsForeignKeys)
 3396               return false;
 3397           switch (action) {
 3398               case ForeignKey.ACTION_RESTRICT:
 3399                   return supportsRestrictDeleteAction;
 3400               case ForeignKey.ACTION_CASCADE:
 3401                   return supportsCascadeDeleteAction;
 3402               case ForeignKey.ACTION_NULL:
 3403                   return supportsNullDeleteAction;
 3404               case ForeignKey.ACTION_DEFAULT:
 3405                   return supportsDefaultDeleteAction;
 3406               default:
 3407                   return false;
 3408           }
 3409       }
 3410   
 3411       /**
 3412        * Whether this database supports the given foreign key update action.
 3413        */
 3414       public boolean supportsUpdateAction(int action) {
 3415           if (action == ForeignKey.ACTION_NONE)
 3416               return true;
 3417           if (!supportsForeignKeys)
 3418               return false;
 3419           switch (action) {
 3420               case ForeignKey.ACTION_RESTRICT:
 3421                   return supportsRestrictUpdateAction;
 3422               case ForeignKey.ACTION_CASCADE:
 3423                   return supportsCascadeUpdateAction;
 3424               case ForeignKey.ACTION_NULL:
 3425                   return supportsNullUpdateAction;
 3426               case ForeignKey.ACTION_DEFAULT:
 3427                   return supportsDefaultUpdateAction;
 3428               default:
 3429                   return false;
 3430           }
 3431       }
 3432   
 3433       /**
 3434        * Return the declaration SQL for the given unique constraint. This
 3435        * method is used from within {@link #getCreateTableSQL}.
 3436        * Returns	<code>CONSTRAINT &lt;name&gt; UNIQUE (&lt;col list&gt;)</code>
 3437        * by default.
 3438        */
 3439       protected String getUniqueConstraintSQL(Unique unq) {
 3440           if (!supportsUniqueConstraints
 3441               || (unq.isDeferred() && !supportsDeferredUniqueConstraints()))
 3442               return null;
 3443   
 3444           StringBuffer buf = new StringBuffer();
 3445           if (unq.getName() != null
 3446               && CONS_NAME_BEFORE.equals(constraintNameMode))
 3447               buf.append("CONSTRAINT ").append(unq.getName()).append(" ");
 3448           buf.append("UNIQUE ");
 3449           if (unq.getName() != null && CONS_NAME_MID.equals(constraintNameMode))
 3450               buf.append(unq.getName()).append(" ");
 3451           buf.append("(").append(Strings.join(unq.getColumns(), ", ")).
 3452               append(")");
 3453           if (unq.isDeferred())
 3454               buf.append(" INITIALLY DEFERRED");
 3455           if (supportsDeferredUniqueConstraints())
 3456               buf.append(" DEFERRABLE");
 3457           if (unq.getName() != null
 3458               && CONS_NAME_AFTER.equals(constraintNameMode))
 3459               buf.append(" CONSTRAINT ").append(unq.getName());
 3460           return buf.toString();
 3461       }
 3462   
 3463       /**
 3464        * Whether or not this dictionary supports deferred unique constraints.
 3465        * This implementation returns {@link #supportsUniqueConstraints}.
 3466        *
 3467        * @since 1.1.0
 3468        */
 3469       protected boolean supportsDeferredUniqueConstraints() {
 3470           return supportsDeferredConstraints;
 3471       }
 3472   
 3473       /////////////////////
 3474       // Database metadata
 3475       /////////////////////
 3476   
 3477       /**
 3478        * This method is used to filter system tables from database metadata.
 3479        * Return true if the given table name represents a system table that
 3480        * should not appear in the schema definition. By default, returns
 3481        * true only if the given table is in the internal list of system tables,
 3482        * or if the given schema is in the list of system schemas and is not
 3483        * the target schema.
 3484        *
 3485        * @param name the table name
 3486        * @param schema the table schema; may be null
 3487        * @param targetSchema if true, then the given schema was listed by
 3488        * the user as one of his schemas
 3489        */
 3490       public boolean isSystemTable(String name, String schema,
 3491           boolean targetSchema) {
 3492           if (systemTableSet.contains(name.toUpperCase()))
 3493               return true;
 3494           return !targetSchema && schema != null
 3495               && systemSchemaSet.contains(schema.toUpperCase());
 3496       }
 3497   
 3498       /**
 3499        * This method is used to filter system indexes from database metadata.
 3500        * Return true if the given index name represents a system index that
 3501        * should not appear in the schema definition. Returns false by default.
 3502        *
 3503        * @param name the index name
 3504        * @param table the index table
 3505        */
 3506       public boolean isSystemIndex(String name, Table table) {
 3507           return false;
 3508       }
 3509   
 3510       /**
 3511        * This method is used to filter system sequences from database metadata.
 3512        * Return true if the given sequence represents a system sequence that
 3513        * should not appear in the schema definition. Returns true if system
 3514        * schema by default.
 3515        *
 3516        * @param name the table name
 3517        * @param schema the table schema; may be null
 3518        * @param targetSchema if true, then the given schema was listed by
 3519        * the user as one of his schemas
 3520        */
 3521       public boolean isSystemSequence(String name, String schema,
 3522           boolean targetSchema) {
 3523           return !targetSchema && schema != null
 3524               && systemSchemaSet.contains(schema.toUpperCase());
 3525       }
 3526   
 3527       /**
 3528        * Reflect on the schema to find tables matching the given name pattern.
 3529        */
 3530       public Table[] getTables(DatabaseMetaData meta, String catalog,
 3531           String schemaName, String tableName, Connection conn)
 3532           throws SQLException {
 3533           if (!supportsSchemaForGetTables)
 3534               schemaName = null;
 3535           else
 3536               schemaName = getSchemaNameForMetadata(schemaName);
 3537   
 3538           String[] types = Strings.split(tableTypes, ",", 0);
 3539           for (int i = 0; i < types.length; i++)
 3540               types[i] = types[i].trim();
 3541   
 3542           beforeMetadataOperation(conn);
 3543           ResultSet tables = null;
 3544           try {
 3545               tables = meta.getTables(getCatalogNameForMetadata(catalog),
 3546                   schemaName, getTableNameForMetadata(tableName), types);
 3547               List tableList = new ArrayList();
 3548               while (tables != null && tables.next())
 3549                   tableList.add(newTable(tables));
 3550               return (Table[]) tableList.toArray(new Table[tableList.size()]);
 3551           } finally {
 3552               if (tables != null)
 3553                   try {
 3554                       tables.close();
 3555                   } catch (Exception e) {
 3556                   }
 3557           }
 3558       }
 3559   
 3560       /**
 3561        * Create a new table from the information in the schema metadata.
 3562        */
 3563       protected Table newTable(ResultSet tableMeta)
 3564           throws SQLException {
 3565           Table t = new Table();
 3566           t.setName(tableMeta.getString("TABLE_NAME"));
 3567           return t;
 3568       }
 3569   
 3570       /**
 3571        * Reflect on the schema to find sequences matching the given name pattern.
 3572        * Returns an empty array by default, as there is no standard way to
 3573        * retrieve a list of sequences.
 3574        */
 3575       public Sequence[] getSequences(DatabaseMetaData meta, String catalog,
 3576           String schemaName, String sequenceName, Connection conn)
 3577           throws SQLException {
 3578           String str = getSequencesSQL(schemaName, sequenceName);
 3579           if (str == null)
 3580               return new Sequence[0];
 3581   
 3582           PreparedStatement stmnt = prepareStatement(conn, str);        
 3583           ResultSet rs = null;
 3584           try {
 3585               int idx = 1;
 3586               if (schemaName != null)
 3587                   stmnt.setString(idx++, schemaName.toUpperCase());
 3588               if (sequenceName != null)
 3589                   stmnt.setString(idx++, sequenceName);
 3590   
 3591               rs = executeQuery(conn, stmnt, str);
 3592               return getSequence(rs);            
 3593            } finally {
 3594               if (rs != null)
 3595                   try {
 3596                       rs.close();
 3597                   } catch (SQLException se) {
 3598                   }
 3599               if (stmnt != null)    
 3600                   try {
 3601                       stmnt.close();
 3602                   } catch (SQLException se) {
 3603                   }
 3604           }
 3605       }
 3606   
 3607       /**
 3608        * Create a new sequence from the information in the schema metadata.
 3609        */
 3610       protected Sequence newSequence(ResultSet sequenceMeta)
 3611           throws SQLException {
 3612           Sequence seq = new Sequence();
 3613           seq.setSchemaName(sequenceMeta.getString("SEQUENCE_SCHEMA"));
 3614           seq.setName(sequenceMeta.getString("SEQUENCE_NAME"));
 3615           return seq;
 3616       }
 3617   
 3618       /**
 3619        * Return the SQL needed to select the list of sequences.
 3620        */
 3621       protected String getSequencesSQL(String schemaName, String sequenceName) {
 3622           return null;
 3623       }
 3624   
 3625       /**
 3626        * Reflect on the schema to find columns matching the given table and
 3627        * column patterns.
 3628        */
 3629       public Column[] getColumns(DatabaseMetaData meta, String catalog,
 3630           String schemaName, String tableName, String columnName, Connection conn)
 3631           throws SQLException {
 3632           if (tableName == null && !supportsNullTableForGetColumns)
 3633               return null;
 3634   
 3635           if (!supportsSchemaForGetColumns)
 3636               schemaName = null;
 3637           else
 3638               schemaName = getSchemaNameForMetadata(schemaName);
 3639   
 3640           beforeMetadataOperation(conn);
 3641           ResultSet cols = null;
 3642           try {
 3643               cols = meta.getColumns(getCatalogNameForMetadata(catalog),
 3644                   schemaName, getTableNameForMetadata(tableName),
 3645                   getColumnNameForMetadata(columnName));
 3646   
 3647               List columnList = new ArrayList();
 3648               while (cols != null && cols.next())
 3649                   columnList.add(newColumn(cols));
 3650               return (Column[]) columnList.toArray
 3651                   (new Column[columnList.size()]);
 3652           } finally {
 3653               if (cols != null)
 3654                   try {
 3655                       cols.close();
 3656                   } catch (Exception e) {
 3657                   }
 3658           }
 3659       }
 3660   
 3661       /**
 3662        * Create a new column from the information in the schema metadata.
 3663        */
 3664       protected Column newColumn(ResultSet colMeta)
 3665           throws SQLException {
 3666           Column c = new Column();
 3667           c.setSchemaName(colMeta.getString("TABLE_SCHEM"));
 3668           c.setTableName(colMeta.getString("TABLE_NAME"));
 3669           c.setName(colMeta.getString("COLUMN_NAME"));
 3670           c.setType(colMeta.getInt("DATA_TYPE"));
 3671           c.setTypeName(colMeta.getString("TYPE_NAME"));
 3672           c.setSize(colMeta.getInt("COLUMN_SIZE"));
 3673           c.setDecimalDigits(colMeta.getInt("DECIMAL_DIGITS"));
 3674           c.setNotNull(colMeta.getInt("NULLABLE")
 3675               == DatabaseMetaData.columnNoNulls);
 3676   
 3677           String def = colMeta.getString("COLUMN_DEF");
 3678           if (!StringUtils.isEmpty(def) && !"null".equalsIgnoreCase(def))
 3679               c.setDefaultString(def);
 3680           return c;
 3681       }
 3682   
 3683       /**
 3684        * Reflect on the schema to find primary keys for the given table pattern.
 3685        */
 3686       public PrimaryKey[] getPrimaryKeys(DatabaseMetaData meta,
 3687           String catalog, String schemaName, String tableName, Connection conn)
 3688           throws SQLException {
 3689           if (useGetBestRowIdentifierForPrimaryKeys)
 3690               return getPrimaryKeysFromBestRowIdentifier(meta, catalog,
 3691                   schemaName, tableName, conn);
 3692           return getPrimaryKeysFromGetPrimaryKeys(meta, catalog,
 3693               schemaName, tableName, conn);
 3694       }
 3695   
 3696       /**
 3697        * Reflect on the schema to find primary keys for the given table pattern.
 3698        */
 3699       protected PrimaryKey[] getPrimaryKeysFromGetPrimaryKeys
 3700           (DatabaseMetaData meta, String catalog, String schemaName,
 3701               String tableName, Connection conn)
 3702           throws SQLException {
 3703           if (tableName == null && !supportsNullTableForGetPrimaryKeys)
 3704               return null;
 3705   
 3706           beforeMetadataOperation(conn);
 3707           ResultSet pks = null;
 3708           try {
 3709               pks = meta.getPrimaryKeys(getCatalogNameForMetadata(catalog),
 3710                   getSchemaNameForMetadata(schemaName),
 3711                   getTableNameForMetadata(tableName));
 3712   
 3713               List pkList = new ArrayList();
 3714               while (pks != null && pks.next())
 3715                   pkList.add(newPrimaryKey(pks));
 3716               return (PrimaryKey[]) pkList.toArray
 3717                   (new PrimaryKey[pkList.size()]);
 3718           } finally {
 3719               if (pks != null)
 3720                   try {
 3721                       pks.close();
 3722                   } catch (Exception e) {
 3723                   }
 3724           }
 3725       }
 3726   
 3727       /**
 3728        * Create a new primary key from the information in the schema metadata.
 3729        */
 3730       protected PrimaryKey newPrimaryKey(ResultSet pkMeta)
 3731           throws SQLException {
 3732           PrimaryKey pk = new PrimaryKey();
 3733           pk.setSchemaName(pkMeta.getString("TABLE_SCHEM"));
 3734           pk.setTableName(pkMeta.getString("TABLE_NAME"));
 3735           pk.setColumnName(pkMeta.getString("COLUMN_NAME"));
 3736           pk.setName(pkMeta.getString("PK_NAME"));
 3737           return pk;
 3738       }
 3739   
 3740       /**
 3741        * Reflect on the schema to find primary keys for the given table pattern.
 3742        */
 3743       protected PrimaryKey[] getPrimaryKeysFromBestRowIdentifier
 3744           (DatabaseMetaData meta, String catalog, String schemaName,
 3745               String tableName, Connection conn)
 3746           throws SQLException {
 3747           if (tableName == null)
 3748               return null;
 3749   
 3750           beforeMetadataOperation(conn);
 3751           ResultSet pks = null;
 3752           try {
 3753               pks = meta.getBestRowIdentifier(catalog, schemaName,
 3754                   tableName, 0, false);
 3755   
 3756               List pkList = new ArrayList();
 3757               while (pks != null && pks.next()) {
 3758                   PrimaryKey pk = new PrimaryKey();
 3759                   pk.setSchemaName(schemaName);
 3760                   pk.setTableName(tableName);
 3761                   pk.setColumnName(pks.getString("COLUMN_NAME"));
 3762                   pkList.add(pk);
 3763               }
 3764               return (PrimaryKey[]) pkList.toArray
 3765                   (new PrimaryKey[pkList.size()]);
 3766           } finally {
 3767               if (pks != null)
 3768                   try {
 3769                       pks.close();
 3770                   } catch (Exception e) {
 3771                   }
 3772           }
 3773       }
 3774   
 3775       /**
 3776        * Reflect on the schema to find indexes matching the given table pattern.
 3777        */
 3778       public Index[] getIndexInfo(DatabaseMetaData meta, String catalog,
 3779           String schemaName, String tableName, boolean unique,
 3780           boolean approx, Connection conn)
 3781           throws SQLException {
 3782           if (tableName == null && !supportsNullTableForGetIndexInfo)
 3783               return null;
 3784   
 3785           beforeMetadataOperation(conn);
 3786           ResultSet indexes = null;
 3787           try {
 3788               indexes = meta.getIndexInfo(getCatalogNameForMetadata(catalog),
 3789                   getSchemaNameForMetadata(schemaName),
 3790                   getTableNameForMetadata(tableName), unique, approx);
 3791   
 3792               List indexList = new ArrayList();
 3793               while (indexes != null && indexes.next())
 3794                   indexList.add(newIndex(indexes));
 3795               return (Index[]) indexList.toArray(new Index[indexList.size()]);
 3796           } finally {
 3797               if (indexes != null)
 3798                   try {
 3799                       indexes.close();
 3800                   } catch (Exception e) {
 3801                   }
 3802           }
 3803       }
 3804   
 3805       /**
 3806        * Create a new index from the information in the schema metadata.
 3807        */
 3808       protected Index newIndex(ResultSet idxMeta)
 3809           throws SQLException {
 3810           Index idx = new Index();
 3811           idx.setSchemaName(idxMeta.getString("TABLE_SCHEM"));
 3812           idx.setTableName(idxMeta.getString("TABLE_NAME"));
 3813           idx.setColumnName(idxMeta.getString("COLUMN_NAME"));
 3814           idx.setName(idxMeta.getString("INDEX_NAME"));
 3815           idx.setUnique(!idxMeta.getBoolean("NON_UNIQUE"));
 3816           return idx;
 3817       }
 3818   
 3819       /**
 3820        * Reflect on the schema to return foreign keys imported by the given
 3821        * table pattern.
 3822        */
 3823       public ForeignKey[] getImportedKeys(DatabaseMetaData meta, String catalog,
 3824           String schemaName, String tableName, Connection conn)
 3825           throws SQLException {
 3826           if (!supportsForeignKeys)
 3827               return null;
 3828           if (tableName == null && !supportsNullTableForGetImportedKeys)
 3829               return null;
 3830   
 3831           beforeMetadataOperation(conn);
 3832           ResultSet keys = null;
 3833           try {
 3834               keys = meta.getImportedKeys(getCatalogNameForMetadata(catalog),
 3835                   getSchemaNameForMetadata(schemaName),
 3836                   getTableNameForMetadata(tableName));
 3837   
 3838               List importedKeyList = new ArrayList();
 3839               while (keys != null && keys.next())
 3840                   importedKeyList.add(newForeignKey(keys));
 3841               return (ForeignKey[]) importedKeyList.toArray
 3842                   (new ForeignKey[importedKeyList.size()]);
 3843           } finally {
 3844               if (keys != null)
 3845                   try {
 3846                       keys.close();
 3847                   } catch (Exception e) {
 3848                   }
 3849           }
 3850       }
 3851   
 3852       /**
 3853        * Create a new foreign key from the information in the schema metadata.
 3854        */
 3855       protected ForeignKey newForeignKey(ResultSet fkMeta)
 3856           throws SQLException {
 3857           ForeignKey fk = new ForeignKey();
 3858           fk.setSchemaName(fkMeta.getString("FKTABLE_SCHEM"));
 3859           fk.setTableName(fkMeta.getString("FKTABLE_NAME"));
 3860           fk.setColumnName(fkMeta.getString("FKCOLUMN_NAME"));
 3861           fk.setName(fkMeta.getString("FK_NAME"));
 3862           fk.setPrimaryKeySchemaName(fkMeta.getString("PKTABLE_SCHEM"));
 3863           fk.setPrimaryKeyTableName(fkMeta.getString("PKTABLE_NAME"));
 3864           fk.setPrimaryKeyColumnName(fkMeta.getString("PKCOLUMN_NAME"));
 3865           fk.setKeySequence(fkMeta.getShort("KEY_SEQ"));
 3866           fk.setDeferred(fkMeta.getShort("DEFERRABILITY")
 3867               == DatabaseMetaData.importedKeyInitiallyDeferred);
 3868   
 3869           int del = fkMeta.getShort("DELETE_RULE");
 3870           switch (del) {
 3871               case DatabaseMetaData.importedKeySetNull:
 3872                   fk.setDeleteAction(ForeignKey.ACTION_NULL);
 3873                   break;
 3874               case DatabaseMetaData.importedKeySetDefault:
 3875                   fk.setDeleteAction(ForeignKey.ACTION_DEFAULT);
 3876                   break;
 3877               case DatabaseMetaData.importedKeyCascade:
 3878                   fk.setDeleteAction(ForeignKey.ACTION_CASCADE);
 3879                   break;
 3880               default:
 3881                   fk.setDeleteAction(ForeignKey.ACTION_RESTRICT);
 3882                   break;
 3883           }
 3884           return fk;
 3885       }
 3886   
 3887       /**
 3888        * Returns the table name that will be used for obtaining information
 3889        * from {@link DatabaseMetaData}.
 3890        */
 3891       protected String getTableNameForMetadata(String tableName) {
 3892           return convertSchemaCase(tableName);
 3893       }
 3894   
 3895       /**
 3896        * Returns the schema name that will be used for obtaining information
 3897        * from {@link DatabaseMetaData}.
 3898        */
 3899       protected String getSchemaNameForMetadata(String schemaName) {
 3900           if (schemaName == null)
 3901               schemaName = conf.getSchema();
 3902           return convertSchemaCase(schemaName);
 3903       }
 3904   
 3905       /**
 3906        * Returns the catalog name that will be used for obtaining information
 3907        * from {@link DatabaseMetaData}.
 3908        */
 3909       protected String getCatalogNameForMetadata(String catalogName) {
 3910           return convertSchemaCase(catalogName);
 3911       }
 3912   
 3913       /**
 3914        * Returns the column name that will be used for obtaining information
 3915        * from {@link DatabaseMetaData}.
 3916        */
 3917       protected String getColumnNameForMetadata(String columnName) {
 3918           return convertSchemaCase(columnName);
 3919       }
 3920   
 3921       /**
 3922        * Convert the specified schema name to a name that the database will
 3923        * be able to understand.
 3924        */
 3925       protected String convertSchemaCase(String objectName) {
 3926           if (objectName == null)
 3927               return null;
 3928   
 3929           String scase = getSchemaCase();
 3930           if (SCHEMA_CASE_LOWER.equals(scase))
 3931               return objectName.toLowerCase();
 3932           if (SCHEMA_CASE_PRESERVE.equals(scase))
 3933               return objectName;
 3934           return objectName.toUpperCase();
 3935       }
 3936       
 3937       /**
 3938        * Return DB specific schemaCase 
 3939        */
 3940       public String getSchemaCase(){
 3941           return schemaCase;
 3942       }
 3943           
 3944       /**
 3945        * Prepared the connection for metadata operations.
 3946        */
 3947       private void beforeMetadataOperation(Connection c) {
 3948           if (requiresAutoCommitForMetaData) {
 3949               try {
 3950                   c.rollback();
 3951               } catch (SQLException sqle) {
 3952               }
 3953               try {
 3954                   if (!c.getAutoCommit())
 3955                       c.setAutoCommit(true);
 3956               } catch (SQLException sqle) {
 3957               }
 3958           }
 3959       }
 3960   
 3961       /////////////////////////////
 3962       // Sequences and Auto-Assign
 3963       /////////////////////////////
 3964   
 3965       /**
 3966        * Return the last generated value for the given column.
 3967        * Throws an exception by default if {@link #lastGeneratedKeyQuery} is null.
 3968        */
 3969       public Object getGeneratedKey(Column col, Connection conn)
 3970           throws SQLException {
 3971           if (lastGeneratedKeyQuery == null)
 3972               throw new StoreException(_loc.get("no-auto-assign"));
 3973   
 3974           // replace things like "SELECT MAX({0}) FROM {1}"
 3975           String query = lastGeneratedKeyQuery;
 3976           if (query.indexOf('{') != -1) // only if the token is in the string
 3977           {
 3978               query = MessageFormat.format(query, new Object[]{
 3979                   col.getName(), getFullName(col.getTable(), false),
 3980                   getGeneratedKeySequenceName(col),
 3981               });
 3982           }
 3983   
 3984           PreparedStatement stmnt = prepareStatement(conn, query);
 3985           ResultSet rs = null;
 3986           try {
 3987               rs = executeQuery(conn, stmnt, query);
 3988               return getKey(rs, col);
 3989           } finally {
 3990               if (rs != null)
 3991                   try { rs.close(); } catch (SQLException se) {}
 3992               if (stmnt != null)    
 3993                   try { stmnt.close(); } catch (SQLException se) {} 
 3994           }
 3995       }
 3996   
 3997       /**
 3998        * Return the sequence name used by databases for the given autoassigned
 3999        * column. This is only used by databases that require an explicit name
 4000        * to be used for auto-assign support.
 4001        */
 4002       protected String getGeneratedKeySequenceName(Column col) {
 4003           String tname = col.getTableName();
 4004           String cname = col.getName();
 4005           int max = maxAutoAssignNameLength;
 4006           int extraChars = -max + tname.length() + 1 // <tname> + '_'
 4007               + cname.length() + 4; // <cname> + '_SEQ'
 4008           if (extraChars > 0) {
 4009               // this assumes that tname is longer than extraChars
 4010               tname = tname.substring(0, tname.length() - extraChars);
 4011           }
 4012           StringBuffer buf = new StringBuffer(max);
 4013           buf.append(tname).append("_").append(cname).append("_SEQ");
 4014           return buf.toString();
 4015       }
 4016   
 4017       ///////////////////////////////
 4018       // Configurable implementation
 4019       ///////////////////////////////
 4020   
 4021       public void setConfiguration(Configuration conf) {
 4022           this.conf = (JDBCConfiguration) conf;
 4023           this.log = this.conf.getLog(JDBCConfiguration.LOG_JDBC);
 4024   
 4025           // warn about unsupported dicts
 4026           if (log.isWarnEnabled() && !isSupported())
 4027               log.warn(_loc.get("dict-not-supported", getClass()));
 4028       }
 4029   
 4030       private boolean isSupported() {
 4031           // if this is a custom dict, traverse to whatever openjpa dict it extends
 4032           Class c = getClass();
 4033           while (!c.getName().startsWith("org.apache.openjpa."))
 4034               c = c.getSuperclass();
 4035   
 4036           // the generic dbdictionary is not considered a supported dict; all
 4037           // other concrete dictionaries are
 4038           if (c == DBDictionary.class)
 4039               return false;
 4040           return true;
 4041       }
 4042   
 4043       public void startConfiguration() {
 4044       }
 4045   
 4046       public void endConfiguration() {
 4047           // initialize the set of reserved SQL92 words from resource
 4048           InputStream in = DBDictionary.class.getResourceAsStream
 4049               ("sql-keywords.rsrc");
 4050           try {
 4051               String keywords = new BufferedReader(new InputStreamReader(in)).
 4052                   readLine();
 4053               in.close();
 4054               reservedWordSet.addAll(Arrays.asList(Strings.split
 4055                   (keywords, ",", 0)));
 4056           } catch (IOException ioe) {
 4057               throw new GeneralException(ioe);
 4058           } finally {
 4059               try { in.close(); } catch (IOException e) {}
 4060           }
 4061   
 4062           // add additional reserved words set by user
 4063           if (reservedWords != null)
 4064               reservedWordSet.addAll(Arrays.asList(Strings.split
 4065                   (reservedWords.toUpperCase(), ",", 0)));
 4066   
 4067           // add system schemas set by user
 4068           if (systemSchemas != null)
 4069               systemSchemaSet.addAll(Arrays.asList(Strings.split
 4070                   (systemSchemas.toUpperCase(), ",", 0)));
 4071   
 4072           // add system tables set by user
 4073           if (systemTables != null)
 4074               systemTableSet.addAll(Arrays.asList(Strings.split
 4075                   (systemTables.toUpperCase(), ",", 0)));
 4076   
 4077           // add fixed size type names set by the user
 4078           if (fixedSizeTypeNames != null)
 4079               fixedSizeTypeNameSet.addAll(Arrays.asList(Strings.split
 4080                   (fixedSizeTypeNames.toUpperCase(), ",", 0)));
 4081           
 4082           // if user has unset sequence sql, null it out so we know sequences
 4083           // aren't supported
 4084           nextSequenceQuery = StringUtils.trimToNull(nextSequenceQuery);
 4085           
 4086           if (selectWords != null)
 4087               selectWordSet.addAll(Arrays.asList(Strings.split(selectWords
 4088                       .toUpperCase(), ",", 0)));
 4089       }
 4090   
 4091       //////////////////////////////////////
 4092       // ConnectionDecorator implementation
 4093       //////////////////////////////////////
 4094   
 4095       /**
 4096        * Decorate the given connection if needed. Some databases require special
 4097        * handling for JDBC bugs. This implementation issues any
 4098        * {@link #initializationSQL} that has been set for the dictionary but
 4099        * does not decoreate the connection.
 4100        */
 4101       public Connection decorate(Connection conn)
 4102           throws SQLException {
 4103           if (!connected)
 4104               connectedConfiguration(conn);
 4105           if (!StringUtils.isEmpty(initializationSQL)) {
 4106               PreparedStatement stmnt = null;
 4107               try {
 4108                   stmnt = conn.prepareStatement(initializationSQL);
 4109                   stmnt.execute();
 4110               } catch (Exception e) {
 4111                   if (log.isTraceEnabled())
 4112                       log.trace(e.toString(), e);
 4113               } finally {
 4114                   if (stmnt != null)
 4115                       try {
 4116                           stmnt.close();
 4117                       } catch (SQLException se) {
 4118                       }
 4119               }
 4120           }
 4121           return conn;
 4122       }
 4123   
 4124       /**
 4125        * Implementation of the
 4126        * {@link LoggingConnectionDecorator.SQLWarningHandler} interface
 4127        * that allows customization of the actions to perform when a
 4128        * {@link SQLWarning} occurs at any point on a {@link Connection},
 4129        * {@link Statement}, or {@link ResultSet}. This method may
 4130        * be used determine those warnings the application wants to
 4131        * consider critical failures, and throw the warning in those
 4132        * cases. By default, this method does nothing.
 4133        *
 4134        * @see LoggingConnectionDecorator#setWarningAction
 4135        * @see LoggingConnectionDecorator#setWarningHandler
 4136        */
 4137       public void handleWarning(SQLWarning warning)
 4138           throws SQLException {
 4139       }
 4140   
 4141       /**
 4142        * Return a new exception that wraps <code>causes</code>.
 4143        * However, the details of exactly what type of exception is returned can
 4144        * be determined by the implementation. This may take into account
 4145        * DB-specific exception information in <code>causes</code>.
 4146        */
 4147       public OpenJPAException newStoreException(String msg, SQLException[] causes,
 4148           Object failed) {
 4149       	if (causes != null && causes.length > 0) {
 4150       		OpenJPAException ret = SQLExceptions.narrow(msg, causes[0], this);
 4151       		ret.setFailedObject(failed).setNestedThrowables(causes);
 4152       		return ret;
 4153       	}
 4154           return new StoreException(msg).setFailedObject(failed).
 4155               setNestedThrowables(causes);
 4156       }
 4157       
 4158       /**
 4159        * Gets the list of String, each represents an error that can help 
 4160        * to narrow down a SQL exception to specific type of StoreException.<br>
 4161        * For example, error code <code>"23000"</code> represents referential
 4162        * integrity violation and hence can be narrowed down to 
 4163        * {@link ReferentialIntegrityException} rather than more general
 4164        * {@link StoreException}.<br>
 4165        * JDBC Drivers are not uniform in return values of SQLState for the same
 4166        * error and hence each database specific Dictionary can specialize.<br>
 4167        * 
 4168        * 
 4169        * @return an <em>unmodifiable</em> list of Strings representing supposedly 
 4170        * uniform SQL States for a given type of StoreException. 
 4171        * Default behavior is to return an empty list.
 4172        */
 4173       public List/*<String>*/ getSQLStates(int exceptionType) {
 4174       	if (exceptionType>=0 && exceptionType<SQL_STATE_CODES.length)
 4175       		return SQL_STATE_CODES[exceptionType];
 4176       	return EMPTY_STRING_LIST;
 4177       }
 4178   
 4179       /**
 4180        * Closes the specified {@link DataSource} and releases any
 4181        * resources associated with it.
 4182        *
 4183        * @param dataSource the DataSource to close
 4184        */
 4185       public void closeDataSource(DataSource dataSource) {
 4186           DataSourceFactory.closeDataSource(dataSource);
 4187       }
 4188   
 4189       /**
 4190        * Used by some mappings to represent data that has already been
 4191        * serialized so that we don't have to serialize multiple times.
 4192        */
 4193       public static class SerializedData {
 4194   
 4195           public final byte[] bytes;
 4196   
 4197           public SerializedData(byte[] bytes) {
 4198               this.bytes = bytes;
 4199           }
 4200       }
 4201       
 4202       /**
 4203        * Return version column name
 4204        * @param column
 4205        * @param tableAlias : this is needed for platform specific version column
 4206        * @return
 4207        */
 4208       public String getVersionColumn(Column column, String tableAlias) {
 4209           return column.toString();
 4210       }
 4211       
 4212       public void insertBlobForStreamingLoad(Row row, Column col, 
 4213           JDBCStore store, Object ob, Select sel) throws SQLException {
 4214           if (ob != null) {
 4215               row.setBinaryStream(col, 
 4216                   new ByteArrayInputStream(new byte[0]), 0);
 4217           } else {
 4218               row.setNull(col);
 4219           }
 4220       }
 4221       
 4222       public void insertClobForStreamingLoad(Row row, Column col, Object ob)
 4223       throws SQLException {
 4224           if (ob != null) {
 4225           row.setCharacterStream(col,
 4226                   new CharArrayReader(new char[0]), 0);
 4227           } else {
 4228               row.setNull(col);
 4229           }
 4230       }
 4231       
 4232       public void updateBlob(Select sel, JDBCStore store, InputStream is)
 4233           throws SQLException {
 4234           SQLBuffer sql = sel.toSelect(true, store.getFetchConfiguration());
 4235           ResultSet res = null;
 4236           Connection conn = store.getConnection();
 4237           PreparedStatement stmnt = null;
 4238           try {
 4239               stmnt = sql.prepareStatement(conn, store.getFetchConfiguration(),
 4240                   ResultSet.TYPE_SCROLL_SENSITIVE,
 4241                   ResultSet.CONCUR_UPDATABLE);
 4242               res = stmnt.executeQuery();
 4243               if (!res.next()) {
 4244                   throw new InternalException(_loc.get("stream-exception"));
 4245               }
 4246               Blob blob = res.getBlob(1);
 4247               OutputStream os = blob.setBinaryStream(1);
 4248               copy(is, os);
 4249               os.close();
 4250               res.updateBlob(1, blob);
 4251               res.updateRow();
 4252   
 4253           } catch (IOException ioe) {
 4254               throw new StoreException(ioe);
 4255           } finally {
 4256               if (res != null)
 4257                   try { res.close (); } catch (SQLException e) {}
 4258               if (stmnt != null)
 4259                   try { stmnt.close (); } catch (SQLException e) {}
 4260               if (conn != null)
 4261                   try { conn.close (); } catch (SQLException e) {}
 4262           }
 4263       }
 4264       
 4265       public void updateClob(Select sel, JDBCStore store, Reader reader)
 4266           throws SQLException {
 4267           SQLBuffer sql = sel.toSelect(true, store.getFetchConfiguration());
 4268           ResultSet res = null;
 4269           Connection conn = store.getConnection();
 4270           PreparedStatement stmnt = null;
 4271           try {
 4272               stmnt = sql.prepareStatement(conn, store.getFetchConfiguration(),
 4273                   ResultSet.TYPE_SCROLL_SENSITIVE,
 4274                   ResultSet.CONCUR_UPDATABLE);
 4275               res = stmnt.executeQuery();
 4276               if (!res.next()) {
 4277                   throw new InternalException(_loc.get("stream-exception"));
 4278               }
 4279               Clob clob = res.getClob(1);
 4280               Writer writer = clob.setCharacterStream(1);
 4281               copy(reader, writer);
 4282               writer.close();
 4283               res.updateClob(1, clob);
 4284               res.updateRow();
 4285   
 4286           } catch (IOException ioe) {
 4287               throw new StoreException(ioe);
 4288           } finally {
 4289               if (res != null) 
 4290                   try { res.close (); } catch (SQLException e) {}
 4291               if (stmnt != null) 
 4292                   try { stmnt.close (); } catch (SQLException e) {}
 4293               if (conn != null) 
 4294                   try { conn.close (); } catch (SQLException e) {}
 4295           }    
 4296       }
 4297       
 4298       protected long copy(InputStream in, OutputStream out) throws IOException {
 4299           byte[] copyBuffer = new byte[blobBufferSize];
 4300           long bytesCopied = 0;
 4301           int read = -1;
 4302   
 4303           while ((read = in.read(copyBuffer, 0, copyBuffer.length)) != -1) {
 4304               out.write(copyBuffer, 0, read);
 4305               bytesCopied += read;
 4306           }
 4307           return bytesCopied;
 4308       }
 4309       
 4310       protected long copy(Reader reader, Writer writer) throws IOException {
 4311           char[] copyBuffer = new char[clobBufferSize];
 4312           long bytesCopied = 0;
 4313           int read = -1;
 4314   
 4315           while ((read = reader.read(copyBuffer, 0, copyBuffer.length)) != -1) {
 4316               writer.write(copyBuffer, 0, read);
 4317               bytesCopied += read;
 4318           }
 4319   
 4320           return bytesCopied;
 4321       }
 4322       
 4323       /**
 4324        * Attach CAST to the current function if necessary
 4325        * 
 4326        * @param val operand value
 4327        * @parma func the sql function statement
 4328        * @return a String with the correct CAST function syntax
 4329        */
 4330       public String getCastFunction(Val val, String func) {
 4331           return func;
 4332       }
 4333       
 4334       /**
 4335        * Create an index if necessary for some database tables
 4336        */
 4337       public void createIndexIfNecessary(Schema schema, String table,
 4338               Column pkColumn) {
 4339       }
 4340       
 4341       /**
 4342        * Return the batchLimit
 4343        */
 4344       public int getBatchLimit(){
 4345           return batchLimit;
 4346       }
 4347       
 4348       /**
 4349        * Set the batchLimit value
 4350        */
 4351       public void setBatchLimit(int limit){
 4352           batchLimit = limit;
 4353       }
 4354       
 4355       /**
 4356        * Validate the batch process. In some cases, we can't batch the statements
 4357        * due to some restrictions. For example, if the GeneratedType=IDENTITY,
 4358        * we have to disable the batch process because we need to get the ID value
 4359        * right away for the in-memory entity to use.
 4360        */
 4361       public boolean validateBatchProcess(RowImpl row, Column[] autoAssign,
 4362               OpenJPAStateManager  sm, ClassMapping cmd ) {
 4363           boolean disableBatch = false;
 4364           if (getBatchLimit()== 0) return false;
 4365           if (autoAssign != null && sm != null) {
 4366               FieldMetaData[] fmd = cmd.getPrimaryKeyFields();
 4367               int i = 0;
 4368               while (!disableBatch && i < fmd.length) {
 4369                   if (fmd[i].getValueStrategy() == ValueStrategies.AUTOASSIGN)
 4370                       disableBatch = true;
 4371                   i++;
 4372               }
 4373           }
 4374           // go to each Dictionary to validate the batch capability
 4375           if (!disableBatch)
 4376               disableBatch = validateDBSpecificBatchProcess(disableBatch, row, 
 4377                   autoAssign, sm, cmd);
 4378           return disableBatch;
 4379       }
 4380       
 4381       /**
 4382        * Allow each Dictionary to validate its own batch process. 
 4383        */
 4384       public boolean validateDBSpecificBatchProcess (boolean disableBatch, 
 4385               RowImpl row, Column[] autoAssign, 
 4386               OpenJPAStateManager  sm, ClassMapping cmd ) {
 4387           return disableBatch;
 4388       }
 4389       
 4390       /**
 4391        * This method is to provide override for non-JDBC or JDBC-like 
 4392        * implementation of executing query.
 4393        */
 4394       protected ResultSet executeQuery(Connection conn, PreparedStatement stmnt, String sql 
 4395           ) throws SQLException {
 4396           return stmnt.executeQuery();
 4397       }
 4398               
 4399       /**
 4400        * This method is to provide override for non-JDBC or JDBC-like 
 4401        * implementation of preparing statement.
 4402        */
 4403       protected PreparedStatement prepareStatement(Connection conn, String sql)
 4404           throws SQLException {
 4405           return conn.prepareStatement(sql);
 4406       }    
 4407    
 4408       /**
 4409        * This method is to provide override for non-JDBC or JDBC-like 
 4410        * implementation of getting sequence from the result set.
 4411        */
 4412       protected Sequence[] getSequence(ResultSet rs) throws SQLException {
 4413           List seqList = new ArrayList();
 4414           while (rs != null && rs.next())
 4415               seqList.add(newSequence(rs));
 4416           return (Sequence[]) seqList.toArray(new Sequence[seqList.size()]);
 4417       }
 4418       
 4419       /**
 4420        * This method is to provide override for non-JDBC or JDBC-like 
 4421        * implementation of getting key from the result set.
 4422        */
 4423       protected Object getKey (ResultSet rs, Column col) throws SQLException {
 4424           if (!rs.next())
 4425               throw new StoreException(_loc.get("no-genkey"));
 4426           Object key = rs.getObject(1);
 4427           if (key == null)
 4428               log.warn(_loc.get("invalid-genkey", col));
 4429           return key;        
 4430       }
 4431       
 4432       /**
 4433        * This method is to provide override for non-JDBC or JDBC-like 
 4434        * implementation of calculating value.
 4435        */
 4436       protected void calculateValue(Val val, Select sel, ExpContext ctx, 
 4437           ExpState state, Path path, ExpState pathState) {
 4438           val.calculateValue(sel, ctx, state, (Val) path, pathState);
 4439       }
 4440   
 4441       /**
 4442        * Determine whether the provided <code>sql</code> may be treated as a 
 4443        * select statement on this database.
 4444        *  
 4445        * @param sql   A sql statement. 
 4446        * 
 4447        * @return true if <code>sql</code> represents a select statement.
 4448        */
 4449       public boolean isSelect(String sql) {
 4450           Iterator i = selectWordSet.iterator();
 4451           String cur;
 4452           while (i.hasNext()) {
 4453               cur = (String) i.next();
 4454               if (sql.length() >= cur.length()
 4455                       && sql.substring(0, cur.length()).equalsIgnoreCase(cur)) {
 4456                   return true;
 4457               }
 4458           }
 4459           return false;
 4460       }
 4461       
 4462       public void deleteStream(JDBCStore store, Select sel) throws SQLException {
 4463           //Do nothing
 4464       }
 4465   }

Save This Page
Home » apache-openjpa-1.1.0-source » org.apache.openjpa.jdbc » sql » [javadoc | source]