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.lang.reflect.Method;
   22   import java.sql.Connection;
   23   import java.sql.DatabaseMetaData;
   24   import java.sql.SQLException;
   25   import java.sql.Types;
   26   import java.util.Arrays;
   27   import java.util.StringTokenizer;
   28   
   29   import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
   30   import org.apache.openjpa.jdbc.kernel.exps.FilterValue;
   31   import org.apache.openjpa.jdbc.kernel.exps.Lit;
   32   import org.apache.openjpa.jdbc.kernel.exps.Param;
   33   import org.apache.openjpa.jdbc.kernel.exps.Val;
   34   import org.apache.openjpa.jdbc.schema.Column;
   35   import org.apache.openjpa.jdbc.schema.Index;
   36   import org.apache.openjpa.jdbc.schema.Schema;
   37   import org.apache.openjpa.jdbc.schema.Sequence;
   38   import org.apache.openjpa.jdbc.schema.Table;
   39   import org.apache.openjpa.kernel.Filters;
   40   import org.apache.openjpa.lib.util.Localizer;
   41   import org.apache.openjpa.meta.JavaTypes;
   42   import org.apache.openjpa.util.OpenJPAException;
   43   import org.apache.openjpa.util.UnsupportedException;
   44   
   45   import serp.util.Strings;
   46   
   47   /**
   48    * Dictionary for IBM DB2 database.
   49    */
   50   public class DB2Dictionary
   51       extends AbstractDB2Dictionary {
   52       private static final Localizer _loc = Localizer.forPackage
   53           (DB2Dictionary.class);
   54   
   55       public String optimizeClause = "optimize for";
   56       public String rowClause = "row";
   57       protected int db2ServerType = 0;
   58       public static final int db2ISeriesV5R3OrEarlier = 1;
   59       public static final int db2UDBV81OrEarlier = 2;
   60       public static final int db2ZOSV8xOrLater = 3;
   61       public static final int db2UDBV82OrLater = 4;
   62       public static final int db2ISeriesV5R4OrLater = 5;
   63   	private static final String forUpdate = "FOR UPDATE";
   64       private static final String withRSClause = "WITH RS";
   65       private static final String withRRClause = "WITH RR";
   66       private static final String useKeepUpdateLockClause
   67           = "USE AND KEEP UPDATE LOCKS";
   68       private static final String useKeepExclusiveLockClause
   69           = "USE AND KEEP EXCLUSIVE LOCKS";
   70       private static final String forReadOnlyClause = "FOR READ ONLY";
   71       protected String databaseProductName = null;
   72       protected String databaseProductVersion = null;
   73       protected int maj = 0;
   74       protected int min = 0;
   75       
   76       private int defaultBatchLimit = 100;
   77   
   78       public DB2Dictionary() {
   79           platform = "DB2";
   80           validationSQL = "SELECT DISTINCT(CURRENT TIMESTAMP) FROM "
   81               + "SYSIBM.SYSTABLES";
   82           supportsSelectEndIndex = true;
   83   
   84           nextSequenceQuery = "VALUES NEXTVAL FOR {0}";
   85   
   86           sequenceSQL = "SELECT SEQSCHEMA AS SEQUENCE_SCHEMA, "
   87               + "SEQNAME AS SEQUENCE_NAME FROM SYSCAT.SEQUENCES";
   88           sequenceSchemaSQL = "SEQSCHEMA = ?";
   89           sequenceNameSQL = "SEQNAME = ?";
   90           characterColumnSize = 254;
   91   
   92           binaryTypeName = "BLOB(1M)";
   93           longVarbinaryTypeName = "BLOB(1M)";
   94           varbinaryTypeName = "BLOB(1M)";
   95           clobTypeName = "CLOB(1M)";
   96           longVarcharTypeName = "LONG VARCHAR";
   97           datePrecision = MICRO;
   98           storeCharsAsNumbers = false;
   99   
  100           fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{
  101               "LONG VARCHAR FOR BIT DATA", "LONG VARCHAR", "LONG VARGRAPHIC",
  102           }));
  103           systemSchemas = new String(
  104                   "SYSCAT,SYSIBM,SYSSTAT,SYSIBMADM,SYSTOOLS");
  105           maxConstraintNameLength = 18;
  106           maxIndexNameLength = 18;
  107           maxColumnNameLength = 30;
  108           supportsDeferredConstraints = false;
  109           supportsDefaultDeleteAction = false;
  110           supportsAlterTableWithDropColumn = false;
  111   
  112           supportsNullTableForGetColumns = false;
  113           requiresCastForMathFunctions = true;
  114           requiresCastForComparisons = true;
  115   
  116           reservedWordSet.addAll(Arrays.asList(new String[]{
  117               "AFTER", "ALIAS", "ALLOW", "APPLICATION", "ASSOCIATE", "ASUTIME",
  118               "AUDIT", "AUX", "AUXILIARY", "BEFORE", "BINARY", "BUFFERPOOL",
  119               "CACHE", "CALL", "CALLED", "CAPTURE", "CARDINALITY", "CCSID",
  120               "CLUSTER", "COLLECTION", "COLLID", "COMMENT", "CONCAT",
  121               "CONDITION", "CONTAINS", "COUNT_BIG", "CURRENT_LC_CTYPE",
  122               "CURRENT_PATH", "CURRENT_SERVER", "CURRENT_TIMEZONE", "CYCLE",
  123               "DATA", "DATABASE", "DAYS", "DB2GENERAL", "DB2GENRL", "DB2SQL",
  124               "DBINFO", "DEFAULTS", "DEFINITION", "DETERMINISTIC", "DISALLOW",
  125               "DO", "DSNHATTR", "DSSIZE", "DYNAMIC", "EACH", "EDITPROC", "ELSEIF",
  126               "ENCODING", "END-EXEC1", "ERASE", "EXCLUDING", "EXIT", "FENCED",
  127               "FIELDPROC", "FILE", "FINAL", "FREE", "FUNCTION", "GENERAL",
  128               "GENERATED", "GRAPHIC", "HANDLER", "HOLD", "HOURS", "IF",
  129               "INCLUDING", "INCREMENT", "INDEX", "INHERIT", "INOUT", "INTEGRITY",
  130               "ISOBID", "ITERATE", "JAR", "JAVA", "LABEL", "LC_CTYPE", "LEAVE",
  131               "LINKTYPE", "LOCALE", "LOCATOR", "LOCATORS", "LOCK", "LOCKMAX",
  132               "LOCKSIZE", "LONG", "LOOP", "MAXVALUE", "MICROSECOND",
  133               "MICROSECONDS", "MINUTES", "MINVALUE", "MODE", "MODIFIES", "MONTHS",
  134               "NEW", "NEW_TABLE", "NOCACHE", "NOCYCLE", "NODENAME", "NODENUMBER",
  135               "NOMAXVALUE", "NOMINVALUE", "NOORDER", "NULLS", "NUMPARTS", "OBID",
  136               "OLD", "OLD_TABLE", "OPTIMIZATION", "OPTIMIZE", "OUT", "OVERRIDING",
  137               "PACKAGE", "PARAMETER", "PART", "PARTITION", "PATH", "PIECESIZE",
  138               "PLAN", "PRIQTY", "PROGRAM", "PSID", "QUERYNO", "READS", "RECOVERY",
  139               "REFERENCING", "RELEASE", "RENAME", "REPEAT", "RESET", "RESIGNAL",
  140               "RESTART", "RESULT", "RESULT_SET_LOCATOR", "RETURN", "RETURNS",
  141               "ROUTINE", "ROW", "RRN", "RUN", "SAVEPOINT", "SCRATCHPAD",
  142               "SECONDS", "SECQTY", "SECURITY", "SENSITIVE", "SIGNAL", "SIMPLE",
  143               "SOURCE", "SPECIFIC", "SQLID", "STANDARD", "START", "STATIC",
  144               "STAY", "STOGROUP", "STORES", "STYLE", "SUBPAGES", "SYNONYM",
  145               "SYSFUN", "SYSIBM", "SYSPROC", "SYSTEM", "TABLESPACE", "TRIGGER",
  146               "TYPE", "UNDO", "UNTIL", "VALIDPROC", "VARIABLE", "VARIANT", "VCAT",
  147               "VOLUMES", "WHILE", "WLM", "YEARS",
  148           }));
  149           
  150           super.setBatchLimit(defaultBatchLimit);
  151           
  152           selectWordSet.add("WITH");
  153       }
  154   
  155       public boolean supportsRandomAccessResultSet(Select sel,
  156           boolean forUpdate) {
  157           return !forUpdate
  158               && super.supportsRandomAccessResultSet(sel, forUpdate);
  159       }
  160   
  161       protected void appendSelectRange(SQLBuffer buf, long start, long end,
  162           boolean subselect) {
  163           // appends the literal range string, since DB2 is unable to handle
  164           // a bound parameter for it
  165           // do not generate FETCH FIRST clause for subselect
  166           if (!subselect)
  167               buf.append(" FETCH FIRST ").append(Long.toString(end)).
  168                   append(" ROWS ONLY");
  169       }
  170   
  171       protected void appendSelect(SQLBuffer selectSQL, Object alias, Select sel,
  172           int idx) {
  173           // if this is a literal value, add a cast...
  174           Object val = sel.getSelects().get(idx);
  175           if (val instanceof Lit)
  176               selectSQL.append("CAST(");
  177   
  178           // ... and add the select per super's behavior...
  179           super.appendSelect(selectSQL, alias, sel, idx);
  180   
  181           // ... and finish the cast
  182           if (val instanceof Lit) {
  183               Class c = ((Lit) val).getType();
  184               int javaTypeCode = JavaTypes.getTypeCode(c);
  185               int jdbcTypeCode = getJDBCType(javaTypeCode, false);
  186               String typeName = getTypeName(jdbcTypeCode);
  187               selectSQL.append(" AS " + typeName);
  188   
  189               // if the literal is a string, use the default char col size
  190               // in the cast statement.
  191               if (String.class.equals(c))
  192                   selectSQL.append("(" + characterColumnSize + ")");
  193   
  194               selectSQL.append(")");
  195           }
  196       }
  197   
  198       public String[] getCreateSequenceSQL(Sequence seq) {
  199           String[] sql = super.getCreateSequenceSQL(seq);
  200           if (seq.getAllocate() > 1)
  201               sql[0] += " CACHE " + seq.getAllocate();
  202           return sql;
  203       }
  204   
  205       protected String getSequencesSQL(String schemaName, String sequenceName) {
  206           StringBuffer buf = new StringBuffer();
  207           buf.append(sequenceSQL);
  208           if (schemaName != null || sequenceName != null)
  209               buf.append(" WHERE ");
  210           if (schemaName != null) {
  211               buf.append(sequenceSchemaSQL);
  212               if (sequenceName != null)
  213                   buf.append(" AND ");
  214           }
  215           if (sequenceName != null)
  216               buf.append(sequenceNameSQL);
  217           return buf.toString();
  218       }
  219   
  220       public Connection decorate(Connection conn)
  221           throws SQLException {
  222           // some versions of the DB2 driver seem to default to
  223           // READ_UNCOMMITTED, which will prevent locking from working
  224           // (multiple SELECT ... FOR UPDATE statements are allowed on
  225           // the same instance); if we have not overridden the
  226           // transaction isolation in the configuration, default to
  227           // TRANSACTION_READ_COMMITTED
  228           conn = super.decorate(conn);
  229   
  230           if (conf.getTransactionIsolationConstant() == -1
  231               && conn.getTransactionIsolation() < conn.TRANSACTION_READ_COMMITTED)
  232               conn.setTransactionIsolation(conn.TRANSACTION_READ_COMMITTED);
  233   
  234           return conn;
  235       }
  236   
  237       private boolean isJDBC3(DatabaseMetaData meta) {
  238           try {
  239               // JDBC3-only method, so it might throw a AbstractMethodError
  240               return meta.getJDBCMajorVersion() >= 3;
  241           } catch (Throwable t) {
  242               return false;
  243           }
  244       }
  245   
  246       public void connectedConfiguration(Connection conn) throws SQLException {
  247       	super.connectedConfiguration(conn);
  248   
  249       	DatabaseMetaData metaData = conn.getMetaData();
  250           databaseProductName = metaData.getDatabaseProductName();
  251           databaseProductVersion = metaData.getDatabaseProductVersion();
  252           
  253           // Determine the type of DB2 database
  254           // First check for AS/400
  255           getProductVersionMajorMinorForISeries();
  256   
  257           if (maj > 0) {
  258               if (isDB2ISeriesV5R3OrEarlier())
  259                   db2ServerType = db2ISeriesV5R3OrEarlier;
  260               else if (isDB2ISeriesV5R4OrLater())
  261                   db2ServerType = db2ISeriesV5R4OrLater;
  262           }
  263           
  264       	if (db2ServerType == 0) {
  265       	    if (isJDBC3(metaData)) {
  266       	        maj = metaData.getDatabaseMajorVersion();
  267       	        min = metaData.getDatabaseMinorVersion();
  268       	    }
  269       	    else
  270       	        getProductVersionMajorMinor();
  271   
  272       	    // Determine the type of DB2 database for ZOS & UDB
  273       	    if (isDB2UDBV81OrEarlier())
  274       	        db2ServerType = db2UDBV81OrEarlier;
  275       	    else if (isDB2ZOSV8xOrLater())
  276       	        db2ServerType = db2ZOSV8xOrLater;
  277       	    else if (isDB2UDBV82OrLater())
  278       	        db2ServerType = db2UDBV82OrLater;
  279           }
  280   
  281           // verify that databae product is supported
  282           if (db2ServerType == 0 || maj == 0)
  283               throw new UnsupportedException(_loc.get("db-not-supported",
  284                   new Object[] {databaseProductName, databaseProductVersion }));                    
  285   
  286       	if (maj >= 9 || (maj == 8 && min >= 2)) {
  287       	    supportsLockingWithMultipleTables = true;
  288       	    supportsLockingWithInnerJoin = true;
  289       	    supportsLockingWithOuterJoin = true;
  290       	    forUpdateClause = "WITH RR USE AND KEEP UPDATE LOCKS";
  291       	    if (maj >=9)
  292       	        supportsXMLColumn = true;
  293       	}
  294   
  295           // platform specific settings
  296           switch (db2ServerType) {
  297           case  db2ZOSV8xOrLater:
  298               // DB2 Z/OS 
  299               characterColumnSize = 255;
  300               lastGeneratedKeyQuery = "SELECT IDENTITY_VAL_LOCAL() FROM "
  301                   + "SYSIBM.SYSDUMMY1";
  302               nextSequenceQuery = "SELECT NEXTVAL FOR {0} FROM "
  303                   + "SYSIBM.SYSDUMMY1";
  304               sequenceSQL = "SELECT SCHEMA AS SEQUENCE_SCHEMA, "
  305                   + "NAME AS SEQUENCE_NAME FROM SYSIBM.SYSSEQUENCES";
  306               sequenceSchemaSQL = "SCHEMA = ?";
  307               sequenceNameSQL = "NAME = ?";
  308               if (maj == 8)
  309                   // DB2 Z/OS Version 8: no bigint support, hence map Java
  310                   // long to decimal
  311                   bigintTypeName = "DECIMAL(31,0)";
  312               break;
  313           case db2ISeriesV5R3OrEarlier:
  314           case db2ISeriesV5R4OrLater:
  315               lastGeneratedKeyQuery = "SELECT IDENTITY_VAL_LOCAL() FROM "
  316                   + "SYSIBM.SYSDUMMY1";
  317               nextSequenceQuery = "SELECT NEXTVAL FOR {0} FROM "
  318                   + "SYSIBM.SYSDUMMY1";
  319               validationSQL = "SELECT DISTINCT(CURRENT TIMESTAMP) FROM "
  320                   + "QSYS2.SYSTABLES";
  321               sequenceSQL = "SELECT SEQUENCE_SCHEMA, "
  322                   + "SEQUENCE_NAME FROM QSYS2.SYSSEQUENCES";
  323               sequenceSchemaSQL = "SEQUENCE_SCHEMA = ?";
  324               sequenceNameSQL = "SEQUENCE_NAME = ?";
  325               break;
  326           }
  327       }
  328   
  329       /**
  330        * Get the update clause for the query based on the
  331        * updateClause and isolationLevel hints
  332        */
  333       protected String getForUpdateClause(JDBCFetchConfiguration fetch,
  334           boolean isForUpdate, Select sel) {
  335           int isolationLevel;
  336           // For db2UDBV81OrEarlier and db2ISeriesV5R3OrEarlier:
  337           // "optimize for" clause appears before "for update" clause.
  338           StringBuffer forUpdateString = new StringBuffer(
  339               getOptimizeClause(sel));
  340           try {
  341               // Determine the isolationLevel; the fetch
  342               // configuration data overrides the persistence.xml value
  343               if (fetch != null && fetch.getIsolation() != -1)
  344                   isolationLevel = fetch.getIsolation();
  345               else
  346                   isolationLevel = conf.getTransactionIsolationConstant();
  347   
  348               if (isForUpdate) {
  349                   switch(db2ServerType) {
  350                   case db2ISeriesV5R3OrEarlier:
  351                   case db2UDBV81OrEarlier:
  352                       if (isolationLevel == Connection.TRANSACTION_SERIALIZABLE)
  353                           forUpdateString.append(" ").append(forUpdateClause);
  354                       else 
  355                           forUpdateString.append(" ").append(forUpdate)
  356                               .append(" ").append(withRSClause);
  357                       break;
  358                   case db2ZOSV8xOrLater:
  359                   case db2UDBV82OrLater:
  360                       if (isolationLevel == Connection.TRANSACTION_SERIALIZABLE) {
  361                           forUpdateString.append(" ").append(forReadOnlyClause)
  362                               .append(" ").append(withRRClause)
  363                               .append(" ").append(useKeepUpdateLockClause);   
  364                       } else {
  365                           forUpdateString.append(" ").append(forReadOnlyClause)
  366                               .append(" ").append(withRSClause)
  367                               .append(" ").append(useKeepUpdateLockClause);                            
  368                       }
  369                       break;
  370                   case db2ISeriesV5R4OrLater:
  371                       if (isolationLevel == Connection.TRANSACTION_SERIALIZABLE) {
  372                           forUpdateString.append(" ").append(forReadOnlyClause)
  373                               .append(" ").append(withRRClause)
  374                               .append(" ").append(useKeepExclusiveLockClause);       
  375                       } else {
  376                           forUpdateString.append(" ").append(forReadOnlyClause)
  377                               .append(" ").append(withRSClause)
  378                               .append(" ").append(useKeepExclusiveLockClause);
  379                       }
  380                       break;
  381                   }
  382               }
  383           }
  384           catch (Exception e) {
  385               if (log.isTraceEnabled())
  386                   log.error(e.toString(),e);
  387           }
  388           return forUpdateString.toString();
  389       }
  390   
  391       public boolean isDB2UDBV82OrLater() {
  392           boolean match = false;
  393           if (databaseProductName != null &&
  394               (databaseProductVersion.indexOf("SQL") != -1
  395               || databaseProductName.indexOf("DB2/") != -1)
  396               && ((maj == 8 && min >= 2) || (maj >= 9)))
  397               match = true;
  398           return match;
  399       }
  400   
  401       public boolean isDB2ZOSV8xOrLater() {
  402          boolean match = false;
  403          if (databaseProductName != null &&
  404              (databaseProductVersion.indexOf("DSN") != -1
  405              || databaseProductName.indexOf("DB2/") == -1)
  406              && maj >= 8)
  407              match = true;
  408           return match;
  409       }
  410   
  411       public boolean isDB2ISeriesV5R3OrEarlier() {
  412          boolean match = false;
  413          if (databaseProductName != null &&
  414              databaseProductName.indexOf("AS") != -1
  415              && ((maj == 5 && min <=3) || maj < 5))
  416              match = true;
  417          return match;
  418       }
  419   
  420       public boolean isDB2ISeriesV5R4OrLater() {
  421          boolean match = false;
  422          if (databaseProductName != null &&
  423              databaseProductName.indexOf("AS") != -1
  424              && (maj >=6 || (maj == 5 && min >=4)))
  425              match = true;
  426         return match;
  427       }
  428   
  429       public boolean isDB2UDBV81OrEarlier() {
  430           boolean match = false;
  431           if (databaseProductName != null &&
  432               (databaseProductVersion.indexOf("SQL") != -1 
  433               || databaseProductName.indexOf("DB2/") != -1) &&
  434               ((maj == 8 && min <= 1) || maj < 8))
  435               match = true;
  436           return match;
  437       }
  438   
  439       /** Get the version Major/Minor for the ISeries
  440        */
  441       private void getProductVersionMajorMinorForISeries() {
  442           // ISeries    DBProdName                 DB2 UDB for AS/400
  443           //   (Toolbox)DBProdVersion              05.04.0000 V5R4m0
  444           // ISeries                               DB2 UDB for AS/400
  445           //   (Native)                            V5R4M0
  446           if (databaseProductName.indexOf("AS") != -1) {
  447               String s = databaseProductVersion.substring(databaseProductVersion
  448                   .indexOf('V'));
  449               s = s.toUpperCase();
  450   
  451               StringTokenizer stringtokenizer = new StringTokenizer(s, "VRM"
  452                   , false);
  453               if (stringtokenizer.countTokens() == 3) {
  454                   String s1 = stringtokenizer.nextToken();
  455                   maj = Integer.parseInt(s1);
  456                   String s2 =  stringtokenizer.nextToken();
  457                   min = Integer.parseInt(s2);
  458               }
  459           }
  460       }
  461       
  462       private void getProductVersionMajorMinor() {
  463           // Incase JDBC driver version is lower than 3
  464           // use following info to determine Major and Minor 
  465           //                        CLI    vs      JCC
  466           // ZDBV8 DBProdName       DB2            DB2
  467           //       DBProdVersion    08.01.0005     DSN08015
  468           // ZDBV9                  DB2            DB2
  469           //                        09.01.0005     DSN09015
  470           // WinV9                  DB2/NT         DB2/NT
  471           //                        09.01.0000     SQL09010
  472           // SolarisV9                             DB2/SUN64
  473           //                                       SQL0901
  474           // Linux                  DB2/LINUX      DB2/LINUX
  475           //                        09.01.0000     SQL0901
  476           if (databaseProductVersion.indexOf("09") != -1) {
  477               maj = 9;
  478               if (databaseProductVersion.indexOf("01") != -1) {
  479                   min = 1;
  480               }
  481           } else if (databaseProductVersion.indexOf("08") != -1) {
  482               maj = 8;
  483               min = 2;
  484               if (databaseProductVersion.indexOf("01") != -1) {
  485                   min = 1;
  486               }
  487           }
  488       }
  489   
  490       protected String getOptimizeClause(Select sel) {
  491           if (sel != null && sel.getExpectedResultCount() > 0) {
  492               StringBuffer buf = new StringBuffer();
  493               buf.append(" ").append(optimizeClause).append(" ")
  494                   .append(String.valueOf(sel.getExpectedResultCount()))
  495                   .append(" ").append(rowClause);
  496               return buf.toString();
  497           }
  498   
  499           return "";
  500       }
  501   
  502       public OpenJPAException newStoreException(String msg, SQLException[] causes,
  503           Object failed) {
  504           if (causes != null && causes.length > 0)
  505               msg = appendExtendedExceptionMsg(msg, causes[0]);
  506           return super.newStoreException(msg, causes, failed);
  507       }
  508   
  509       /**
  510        *  Append exception information from SQLCA to the exsisting
  511        *  exception meassage
  512        */
  513       private String appendExtendedExceptionMsg(String msg, SQLException sqle){
  514          final String GETSQLCA ="getSqlca";
  515          String exceptionMsg = new String();
  516          try {
  517               Method sqlcaM2 = sqle.getNextException().getClass()
  518                                .getMethod(GETSQLCA,null);
  519               Object sqlca = sqlcaM2.invoke(sqle.getNextException(),
  520                                             new Object[] {});
  521               Method  getSqlErrpMethd = sqlca.getClass().
  522               getMethod("getSqlErrp", null);
  523               Method  getSqlWarnMethd = sqlca.getClass().
  524               getMethod("getSqlWarn", null);
  525               Method  getSqlErrdMethd = sqlca.getClass().
  526               getMethod("getSqlErrd", null);
  527               StringBuffer errdStr = new StringBuffer();
  528   
  529               int[] errds = (int[]) getSqlErrdMethd.invoke(sqlca, new Object[]{});
  530               for (int i = 0; i < errds.length; i++)
  531                   errdStr.append(errdStr.length() > 0 ? ", " : "").
  532                       append(errds[i]);
  533               exceptionMsg = exceptionMsg.concat( "SQLCA OUTPUT" +
  534                       "[Errp=" + getSqlErrpMethd.invoke(sqlca, new Object[]{})
  535                       + ", Errd=" + errdStr);
  536   
  537               String Warn = new String((char[]) getSqlWarnMethd.
  538                       invoke(sqlca, new Object[]{}));
  539               if(Warn.trim().length() != 0)
  540                   exceptionMsg = exceptionMsg.concat(", Warn=" +Warn + "]" );
  541               else
  542                   exceptionMsg = exceptionMsg.concat( "]" );
  543               msg = msg.concat(exceptionMsg);
  544               return msg;
  545           } catch (Throwable t) {
  546               return sqle.getMessage();
  547           }
  548       }
  549   
  550       public int getDb2ServerType() {
  551           return db2ServerType;
  552       }
  553       
  554       protected void appendLength(SQLBuffer buf, int type) {
  555           if (type == Types.VARCHAR)
  556               buf.append("(").append(Integer.toString(characterColumnSize)).
  557                   append(")");
  558       }
  559   
  560       /**
  561        * If this dictionary supports XML type,
  562        * use this method to append xml predicate.
  563        * 
  564        * @param buf the SQL buffer to write the comparison
  565        * @param op the comparison operation to perform
  566        * @param lhs the left hand side of the comparison
  567        * @param rhs the right hand side of the comparison
  568        * @param lhsxml indicates whether the left operand maps to xml
  569        * @param rhsxml indicates whether the right operand maps to xml
  570        */
  571       public void appendXmlComparison(SQLBuffer buf, String op, FilterValue lhs,
  572           FilterValue rhs, boolean lhsxml, boolean rhsxml) {
  573           super.appendXmlComparison(buf, op, lhs, rhs, lhsxml, rhsxml);
  574           if (lhsxml && rhsxml)
  575               appendXmlComparison2(buf, op, lhs, rhs);
  576           else if (lhsxml)
  577               appendXmlComparison1(buf, op, lhs, rhs);
  578           else 
  579               appendXmlComparison1(buf, op, rhs, lhs);
  580       }
  581   
  582       /**
  583        * Append an xml comparison predicate.
  584        *
  585        * @param buf the SQL buffer to write the comparison
  586        * @param op the comparison operation to perform
  587        * @param lhs the left hand side of the comparison (maps to xml column)
  588        * @param rhs the right hand side of the comparison
  589        */
  590       private void appendXmlComparison1(SQLBuffer buf, String op, 
  591               FilterValue lhs, FilterValue rhs) {
  592           boolean castrhs = false;
  593           Class rc = Filters.wrap(rhs.getType());
  594           int type = 0;
  595           if (rhs.isConstant()) {
  596               type = getJDBCType(JavaTypes.getTypeCode(rc), false);
  597               castrhs = true;
  598           }
  599           
  600           appendXmlExists(buf, lhs);
  601   
  602           buf.append(" ").append(op).append(" ");
  603           
  604           buf.append("$");
  605           if (castrhs)
  606               buf.append("Parm");
  607           else
  608               rhs.appendTo(buf);
  609           
  610           buf.append("]' PASSING ");
  611           appendXmlVar(buf, lhs);
  612           buf.append(", ");
  613           
  614           if (castrhs)
  615               appendCast(buf, rhs, type);
  616           else
  617               rhs.appendTo(buf);
  618           
  619           buf.append(" AS \"");
  620           if (castrhs)
  621               buf.append("Parm");
  622           else
  623               rhs.appendTo(buf);
  624           buf.append("\")");
  625       }
  626       
  627       /**
  628        * Append an xml comparison predicate. (both operands map to xml column)
  629        *
  630        * @param buf the SQL buffer to write the comparison
  631        * @param op the comparison operation to perform
  632        * @param lhs the left hand side of the comparison (maps to xml column)
  633        * @param rhs the right hand side of the comparison (maps to xml column)
  634        */
  635       private void appendXmlComparison2(SQLBuffer buf, String op, 
  636               FilterValue lhs, FilterValue rhs) {
  637           appendXmlExists(buf, lhs);
  638           
  639           buf.append(" ").append(op).append(" ");
  640           
  641           buf.append("$").append(rhs.getColumnAlias(
  642               rhs.getFieldMapping().getColumns()[0])).
  643               append("/*/");
  644           rhs.appendTo(buf);
  645           
  646           buf.append("]' PASSING ");
  647           appendXmlVar(buf, lhs);
  648           buf.append(", ");
  649           appendXmlVar(buf, rhs);
  650           buf.append(")");
  651       }
  652       
  653       private void appendXmlVar(SQLBuffer buf, FilterValue val) {
  654           buf.append(val.getColumnAlias(
  655               val.getFieldMapping().getColumns()[0])).
  656               append(" AS ").
  657               append("\"").append(val.getColumnAlias(
  658               val.getFieldMapping().getColumns()[0])).
  659               append("\"");        
  660       }
  661       
  662       private void appendXmlExists(SQLBuffer buf, FilterValue val) {
  663           buf.append("XMLEXISTS('");
  664           buf.append("$").append(val.getColumnAlias(
  665               val.getFieldMapping().getColumns()[0])).
  666               append("/*[");
  667           val.appendTo(buf);        
  668       }
  669       
  670       /**
  671        * add CAST for a scalar function where operand is a param
  672        * 
  673        * @param func original string
  674        * @param target substring to look for
  675        * @param asString 
  676        * @return updated string (func)
  677        */
  678       private String addCastAsString(String func, String target, 
  679               String asString) {
  680           String fstring = func;
  681           if (func.indexOf(target) != -1)
  682               fstring = Strings.replace(
  683                   func, target, "CAST(" + target + asString + ")");
  684           return fstring;
  685       }
  686   
  687       /**
  688        * add CAST for a function operator where operand is a param
  689        * 
  690        * @param func function name
  691        * @param val type
  692        * @return updated string (func)
  693        */
  694       public String addCastAsType(String func, Val val) {
  695           String fstring = null;
  696           String type = getTypeName(getJDBCType(JavaTypes.getTypeCode(val
  697               .getType()), false));
  698           if (String.class.equals(val.getType()))
  699               type = type + "(" + characterColumnSize + ")";
  700           fstring = "CAST(? AS " + type + ")";
  701           return fstring;
  702       }
  703   
  704       /**
  705        * Return the batch limit. If the batchLimit is -1, change it to 100 for
  706        * best performance
  707        */
  708       public int getBatchLimit() {
  709           int limit = super.getBatchLimit();
  710           if (limit == UNLIMITED) {
  711               limit = defaultBatchLimit;
  712               if (log.isTraceEnabled())
  713                   log.trace(_loc.get("batch_unlimit", String.valueOf(limit)));
  714           }
  715           return limit;
  716       }
  717   
  718       /**
  719        * Return the correct CAST function syntax
  720        * 
  721        * @param val operand of cast
  722        * @param func original string
  723        * @return a String with the correct CAST function syntax
  724        */
  725       public String getCastFunction(Val val, String func) {
  726           if (val instanceof Lit || val instanceof Param)
  727               if (func.indexOf("VARCHAR") == -1)
  728                   func = addCastAsString(func, "{0}", " AS VARCHAR(1000)");
  729           return func;
  730       }
  731   
  732       public void indexOf(SQLBuffer buf, FilterValue str, FilterValue find,
  733               FilterValue start) {
  734           if (find.getValue() != null) { // non constants
  735               buf.append("(LOCATE(CAST((");
  736               find.appendTo(buf);
  737               buf.append(") AS VARCHAR(1000)), ");
  738           } else {
  739               // this is a constant
  740               buf.append("(LOCATE(");
  741               find.appendTo(buf);
  742               buf.append(", ");
  743           }
  744           if (str.getValue() != null) {
  745               buf.append("CAST((");
  746               str.appendTo(buf);
  747               buf.append(") AS VARCHAR(1000))");
  748           } else {
  749               str.appendTo(buf);
  750           }
  751           if (start != null) {
  752               if (start.getValue() == null) {
  753                   buf.append(", CAST((");
  754                   start.appendTo(buf);
  755                   buf.append(") AS INTEGER) + 1");
  756               } else {
  757                   buf.append(", ");
  758                   start.appendTo(buf);
  759               }
  760           }
  761           buf.append(") - 1)");
  762       }
  763       
  764       /** 
  765        * Cast the specified value to the specified type.
  766        *
  767        * @param buf the buffer to append the cast to
  768        * @param val the value to cast
  769        * @param type the type of the case, e.g. {@link Types#NUMERIC}
  770        */
  771       public void appendCast(SQLBuffer buf, FilterValue val, int type) {
  772   
  773           // Convert the cast function: "CAST({0} AS {1})"
  774           int firstParam = castFunction.indexOf("{0}");
  775           String pre = castFunction.substring(0, firstParam); // "CAST("
  776           String mid = castFunction.substring(firstParam + 3);
  777           int secondParam = mid.indexOf("{1}");
  778           String post;
  779           if (secondParam > -1) {
  780               post = mid.substring(secondParam + 3); // ")"
  781               mid = mid.substring(0, secondParam); // " AS "
  782           } else
  783               post = "";
  784   
  785           // No need to add CAST if the value is a constant
  786           if (val instanceof Lit || val instanceof Param) {
  787               buf.append(pre);
  788               val.appendTo(buf);
  789               buf.append(mid);
  790               buf.append(getTypeName(type));
  791               appendLength(buf, type);
  792               buf.append(post);
  793           } else {
  794               val.appendTo(buf);
  795               String sqlString = buf.getSQL(false);
  796               if (sqlString.endsWith("?")) {
  797                   // case "(?" - convert to "CAST(? AS type"
  798                   String typeName = getTypeName(type);
  799                   if (String.class.equals(val.getType()))
  800                       typeName = typeName + "(" + characterColumnSize + ")";
  801                   String str = "CAST(? AS " + typeName + ")";
  802                   buf.replaceSqlString(sqlString.length() - 1,
  803                           sqlString.length(), str);
  804               }
  805           }
  806       }
  807   
  808       /**
  809        * Create an index if necessary for some database tables
  810        */
  811       public void createIndexIfNecessary(Schema schema, String table,
  812               Column pkColumn) {
  813           if (isDB2ZOSV8xOrLater()) {
  814               // build the index for the sequence tables
  815               // the index name will the fully qualified table name + _IDX
  816               Table tab = schema.getTable(table);
  817               Index idx = tab.addIndex(tab.getFullName() + "_IDX");
  818               idx.setUnique(true);
  819               idx.addColumn(pkColumn);
  820           }
  821       }
  822   }

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