JAVA   51

Oracle extends SQL92

Guest on 11th May 2022 04:46:41 PM

  1. package de.fuberlin.wiwiss.d2rq.sql.vendor;
  2.  
  3. import java.lang.reflect.Method;
  4. import java.sql.Connection;
  5. import java.sql.ResultSet;
  6. import java.sql.SQLException;
  7. import java.sql.Statement;
  8. import java.sql.Timestamp;
  9. import java.sql.Types;
  10. import java.text.DateFormat;
  11. import java.text.SimpleDateFormat;
  12. import java.util.Arrays;
  13. import java.util.Calendar;
  14. import java.util.GregorianCalendar;
  15. import java.util.TimeZone;
  16. import java.util.regex.Pattern;
  17.  
  18. import de.fuberlin.wiwiss.d2rq.D2RQException;
  19. import de.fuberlin.wiwiss.d2rq.expr.BooleanToIntegerCaseExpression;
  20. import de.fuberlin.wiwiss.d2rq.expr.Expression;
  21. import de.fuberlin.wiwiss.d2rq.expr.SQLExpression;
  22. import de.fuberlin.wiwiss.d2rq.map.Database;
  23. import de.fuberlin.wiwiss.d2rq.sql.types.DataType;
  24. import de.fuberlin.wiwiss.d2rq.sql.types.SQLApproximateNumeric;
  25. import de.fuberlin.wiwiss.d2rq.sql.types.SQLBinary;
  26. import de.fuberlin.wiwiss.d2rq.sql.types.SQLCharacterString;
  27. import de.fuberlin.wiwiss.d2rq.sql.types.SQLTimestamp;
  28. import de.fuberlin.wiwiss.d2rq.sql.types.UnsupportedDataType;
  29.  
  30. /**
  31.  * This syntax class implements MySQL-compatible SQL syntax.
  32.  *
  33.  * @author Richard Cyganiak
  34.  */
  35. public class Oracle extends SQL92 {
  36.  
  37.         public Oracle() {
  38.                 super(false);
  39.         }
  40.        
  41.         @Override
  42.         public Expression getRowNumLimitAsExpression(int limit) {
  43.                 if (limit == Database.NO_LIMIT) return Expression.TRUE;
  44.                 return SQLExpression.create("ROWNUM <= " + limit);
  45.         }
  46.  
  47.         @Override
  48.         public String getRowNumLimitAsQueryAppendage(int limit) {
  49.                 return "";
  50.         }
  51.        
  52.         @Override
  53.         public String quoteBinaryLiteral(String hexString) {
  54.                 return quoteStringLiteral(hexString);
  55.         }
  56.  
  57.         @Override
  58.         public DataType getDataType(int jdbcType, String name, int size) {
  59.                
  60.                 // Doesn't support DISTINCT over LOB types
  61.                 if (jdbcType == Types.CLOB || "NCLOB".equals(name)) {
  62.                         return new SQLCharacterString(this, name, false);
  63.                 }
  64.                 if (jdbcType == Types.BLOB) {
  65.                         return new SQLBinary(this, name, false);
  66.                 }
  67.                
  68.                 DataType standard = super.getDataType(jdbcType, name, size);
  69.                 if (standard != null) return standard;
  70.  
  71.                 // Special handling for TIMESTAMP(x) WITH LOCAL TIME ZONE
  72.                 if (name.contains("WITH LOCAL TIME ZONE") || "TIMESTAMPLTZ".equals(name)) {
  73.                         return new OracleCompatibilityTimeZoneLocalDataType(this, name);
  74.                 }
  75.                
  76.                 // Special handling for TIMESTAMP(x) WITH TIME ZONE
  77.                 if(name.contains("WITH TIME ZONE") || "TIMESTAMPTZ".equals(name)) {
  78.                         return new OracleCompatibilityTimeZoneDataType(this, name);
  79.                 }
  80.                
  81.                 // Oracle-specific character string types
  82.                 if ("VARCHAR2".equals(name) || "NVARCHAR2".equals(name)) {
  83.                         return new SQLCharacterString(this, name, true);
  84.                 }
  85.  
  86.                 // Oracle-specific floating point types
  87.         if ("BINARY_FLOAT".equals(name) || "BINARY_DOUBLE".equals(name)) {
  88.                 return new SQLApproximateNumeric(this, name);
  89.         }
  90.        
  91.                 // Oracle binary file pointer
  92.                 // TODO: We could at least support reading from BFILE, although querying for them seems hard
  93.         if ("BFILE".equals(name)) {
  94.                 return new UnsupportedDataType(jdbcType, name);
  95.         }
  96.  
  97.         return null;
  98.         }
  99.  
  100.         @Override
  101.         public void initializeConnection(Connection connection) throws SQLException {
  102.                 // Set Oracle date formats
  103.                 Statement stmt = connection.createStatement();
  104.                 try {
  105.                         stmt.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'SYYYY-MM-DD'");
  106.                         stmt.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'SYYYY-MM-DD HH24:MI:SS'");
  107.                         setSessionTimeZone(connection, getTimeZoneForSession().getID());
  108.                 } catch (Exception ex) {
  109.                         throw new D2RQException(ex);
  110.                 } finally {
  111.                         stmt.close();
  112.                 }
  113.         }
  114.  
  115.         /**
  116.          * Sets the session time zone on the connection. We need to call an
  117.          * Oracle-specific method. We use reflection for this so we can compile
  118.          * the code even without the Oracle driver on the classpath. This method
  119.          * does:
  120.          *
  121.          * ((OracleConnection) connection).setSessionTimeZone(timeZoneID);
  122.          */
  123.         private void setSessionTimeZone(Connection connection, String timeZoneID)
  124.                         throws Exception {
  125.                 Class<?> c = Class.forName("oracle.jdbc.driver.OracleConnection");
  126.                 Method setSessionTimeZone = c.getMethod("setSessionTimeZone", String.class);
  127.                 setSessionTimeZone.invoke(connection, timeZoneID);
  128.         }
  129.        
  130.         /**
  131.          * A separate method for this just to highlight that it needs to
  132.          * be used in multiple places.
  133.          */
  134.         private static TimeZone getTimeZoneForSession() {
  135.                 return TimeZone.getDefault();
  136.         }
  137.        
  138.         /**
  139.         * Oracle doesn't actually support booolean expressions, except in
  140.         * a few places. Turn the boolean into an int using a CASE statement
  141.         */
  142.         public Expression booleanExpressionToSimpleExpression(Expression expression) {
  143.             return new BooleanToIntegerCaseExpression(expression);
  144.         }
  145.        
  146.         @Override
  147.         public boolean isIgnoredTable(String schema, String table) {
  148.                 if (Arrays.binarySearch(IGNORED_SCHEMAS, schema) >= 0) return true;
  149.                 if (IGNORED_SCHEMAS_PATTERN.matcher(schema).matches()) return true;
  150.                 // Skip Oracle system schemas as well as deleted tables in Oracle's Recycling Bin.
  151.                 // The latter have names like MYSCHEMA.BIN$FoHqtx6aQ4mBaMQmlTCPTQ==$0
  152.                 if (table.startsWith("BIN$")) return true;
  153.                 // Skip nested tables with names like SYS_NTr8Sify7K5pLgQBmQXa5h2g==
  154.                 // There are issues where the table cannot be found later
  155.                 if (table.startsWith("SYS_NT")) return true;
  156.                 return false;
  157.         }
  158.         private static final String[] IGNORED_SCHEMAS = {
  159.                 "APPQOSSYS", "CACHEADM", "CTXSYS", "DBSNMP", "EXFSYS", "FLOWS_FILES",
  160.                 "MDSYS", "OLAPSYS", "ORDDATA", "ORDSYS", "OUTLN", "OWBSYS",
  161.                 "SYS", "SYSMAN", "SYSTEM", "TIMESTEN", "WKSYS", "WK_TEST", "WMSYS",
  162.                 "XDB", "XDBEXT", "XDBPM"};
  163.         private final static Pattern IGNORED_SCHEMAS_PATTERN = Pattern.compile("(APEX|FLOWS)_\\d{6}");
  164.        
  165.         /**
  166.          * getString() doesn't really work for TIMESTAMP WITH LOCAL TIME ZONE,
  167.          * we have to use getTimestamp() and format the resulting Timestamp object
  168.          * according to the session's time zone.
  169.          *
  170.          * @author Aftab Iqbal
  171.          */
  172.         public static class OracleCompatibilityTimeZoneLocalDataType extends SQLTimestamp {
  173.                 public OracleCompatibilityTimeZoneLocalDataType(Vendor syntax, String name) {
  174.                         super(syntax, name);
  175.                 }
  176.                
  177.                 @Override
  178.                 public String value(ResultSet resultSet, int column) throws SQLException {
  179.                         // Hack for TIMESTAMP WITH LOCAL TIME ZONE data type
  180.                         Timestamp timestampValue = resultSet.getTimestamp(column);
  181.                         return formatForSessionTimeZone(timestampValue);
  182.                 }
  183.                
  184.                 /**
  185.                  * @param timestamp A timestamp (does not contain time zone information)
  186.                  * @return Formatted as xsd:dateTime in the session's time zone
  187.                  */
  188.                 private static String formatForSessionTimeZone(Timestamp timestamp) {
  189.                 Calendar cal = new GregorianCalendar();
  190.                 cal.setTime(timestamp);
  191.                 DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
  192.                 df.setTimeZone(getTimeZoneForSession());
  193.                 String dateTime = df.format(cal.getTime());
  194.        
  195.                 // Adding colon into time zone value (e.g. +03:00)
  196.                 // as required by xsd:dateTime. SimpleDateFormat can't do it on its own
  197.                 return dateTime.substring(0, dateTime.length()-2) +
  198.                                 ":" + dateTime.substring(dateTime.length()-2);
  199.             }
  200.         }
  201.        
  202.         public static class OracleCompatibilityTimeZoneDataType extends SQLTimestamp {
  203.                 public OracleCompatibilityTimeZoneDataType(Vendor syntax, String name) {
  204.                         super(syntax, name);
  205.                 }
  206.                 @Override
  207.                 public String value(ResultSet resultSet, int column) throws SQLException {
  208.                         // Hack for Oracle TIMESTAMP WITH TIME ZONE data type
  209.                         try {
  210.                                 return super.value(resultSet, column);
  211.                         } catch (SQLException ex) {
  212.                                 return null;
  213.                         }
  214.                 }
  215.         }
  216. }

Raw Paste


Login or Register to edit or fork this paste. It's free.