/****************************************************************************** * Product: Adempiere ERP & CRM Smart Business Solution * * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * * under the terms version 2 of the GNU General Public License as published * * by the Free Software Foundation. This program is distributed in the hope * * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., * * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * For the text or an alternative of this public license, you may reach us * * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * * or via info@compiere.org or http://www.compiere.org/license.html * *****************************************************************************/ package org.compiere.db; import java.io.File; import java.io.InputStream; import java.math.BigDecimal; import java.math.RoundingMode; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.Properties; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import javax.sql.DataSource; import org.adempiere.db.oracle.OracleBundleActivator; import org.adempiere.db.oracle.partition.TablePartitionService; import org.adempiere.exceptions.DBException; import org.compiere.db.partition.ITablePartitionService; import org.compiere.dbPort.Convert; import org.compiere.dbPort.Convert_Oracle; import org.compiere.model.MColumn; import org.compiere.model.MTable; import org.compiere.model.PO; import org.compiere.model.SystemProperties; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.DisplayType; import org.compiere.util.Ini; import org.compiere.util.Language; import org.compiere.util.Trx; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariPoolMXBean; import oracle.jdbc.OracleDriver; /** * Oracle Database Port * * @author Jorg Janke * @version $Id: DB_Oracle.java,v 1.7 2006/09/22 23:35:19 jjanke Exp $ * --- * Modifications: Refactoring. Replaced Oracle Cache Manager with C3P0 * connection pooling framework for better and more efficient connection handling * * @author Ashley Ramdass (Posterita) */ public class DB_Oracle implements AdempiereDatabase { private static final String POOL_PROPERTIES = "hikaricp.properties"; /** * Oracle Database */ public DB_Oracle() { /** Causes VPN problems ??? try { getDriver(); } catch (Exception e) { log.log(Level.SEVERE, e.getMessage()); } **/ // teo [ bug 1638208 ]: oracle 10g DATETIME issue // http://www.oracle.com/technology/tech/java/sqlj_jdbc/htdocs/jdbc_faq.htm#08_01 try { System.setProperty("oracle.jdbc.V8Compatible", "true"); } catch (Exception e) { log.log(Level.SEVERE, e.getMessage()); } } // DB_Oracle /** Static Driver */ private static OracleDriver s_driver = null; /** Driver Class Name */ public static final String DRIVER = "oracle.jdbc.OracleDriver"; /** Default Port */ public static final int DEFAULT_PORT = 1521; /** Default Connection Manager Port */ public static final int DEFAULT_CM_PORT = 1630; /** Connection String */ private volatile String m_connectionURL; /** Data Source */ private volatile HikariDataSource m_ds; /** Cached User Name */ private String m_userName = null; private Convert m_convert = new Convert_Oracle(); /** Logger */ private static final CLogger log = CLogger.getCLogger (DB_Oracle.class); /** * Get Database Name * @return database short name */ public String getName() { return Database.DB_ORACLE; } // getName /** * Get Database Description * @return database long name and version */ public String getDescription() { try { if (s_driver == null) getDriver(); } catch (Exception e) { } if (s_driver != null) return s_driver.toString(); return "No Driver"; } // getDescription /** * Get Standard JDBC Port * @return standard port */ public int getStandardPort() { return DEFAULT_PORT; } // getStandardPort /** * Get and register Database Driver * @return Driver * @throws SQLException */ public synchronized Driver getDriver() throws SQLException { if (s_driver == null) { // Speed up transfer rate System.setProperty("oracle.jdbc.TcpNoDelay", "true"); // Oracle Multi - Language System.setProperty("oracle.jdbc.defaultNChar", "true"); // s_driver = new OracleDriver(); DriverManager.registerDriver (s_driver); DriverManager.setLoginTimeout (Database.CONNECTION_TIMEOUT); } return s_driver; } // getDriver /** * Get Database Connection String. *
     *  Timing:
     *  - CM with source_route not in address_list  = 28.5 sec
     *  - CM with source_route in address_list      = 58.0 sec
     *  - direct    = 4.3-8 sec  (no real difference if on other box)
     *  - bequeath  = 3.4-8 sec
     *  
* @param connection Connection Descriptor * @return connection String */ public String getConnectionURL (CConnection connection) { System.setProperty("oracle.jdbc.v$session.program", "iDempiere"); StringBuilder sb = null; // Server Connections (bequeath) if (connection.isBequeath()) { sb = new StringBuilder("jdbc:oracle:oci8:@"); // bug: does not work if there is more than one db instance - use Net8 // sb.append(connection.getDbName()); } else // thin driver { sb = new StringBuilder("jdbc:oracle:thin:@"); // direct connection if (connection.isViaFirewall()) { // (description=(address_list= // ( (source_route=yes) // (address=(protocol=TCP)(host=cmhost)(port=1630)) // (address=(protocol=TCP)(host=dev)(port=1521)) // (connect_data=(service_name=dev1.adempiere.org))) sb.append("(DESCRIPTION=(ADDRESS_LIST=") .append("(SOURCE_ROUTE=YES)") .append("(ADDRESS=(PROTOCOL=TCP)(HOST=").append(connection.getFwHost()) .append(")(PORT=").append(connection.getFwPort()).append("))") .append("(ADDRESS=(PROTOCOL=TCP)(HOST=").append(connection.getDbHost()) .append(")(PORT=").append(connection.getDbPort()).append(")))") .append("(CONNECT_DATA=(SERVICE_NAME=").append(connection.getDbName()).append(")))"); } else { // old: jdbc:oracle:thin:@dev2:1521:sid // new: jdbc:oracle:thin:@//dev2:1521/serviceName sb.append("//") .append(connection.getDbHost()) .append(":").append(connection.getDbPort()) .append("/").append(connection.getDbName()); } } m_connectionURL = sb.toString(); // log.config(m_connectionURL); // m_userName = connection.getDbUid(); return m_connectionURL; } // getConnectionURL /** * Get Connection URL. * http://download-east.oracle.com/docs/cd/B14117_01/java.101/b10979/urls.htm#BEIDBFDF * @param dbHost db Host * @param dbPort db Port * @param dbName db Name * @param userName user name * @return connection */ public String getConnectionURL (String dbHost, int dbPort, String dbName, String userName) { m_userName = userName; m_connectionURL = "jdbc:oracle:thin:@//" + dbHost + ":" + dbPort + "/" + dbName; return m_connectionURL; } // getConnectionURL /** * Get Database Connection String * @param connectionURL Connection URL * @param userName user name * @return connection String */ public String getConnectionURL (String connectionURL, String userName) { m_userName = userName; m_connectionURL = connectionURL; return m_connectionURL; } // getConnectionURL /** * Get JDBC Catalog * @return null - not used */ public String getCatalog() { return null; } // getCatalog /** * Get JDBC Schema * @return user name */ public String getSchema() { if (m_userName != null) return m_userName.toUpperCase(); log.severe("User Name not set (yet) - call getConnectionURL first"); return null; } // getSchema /** * Supports BLOB * @return true if BLOB is supported */ public boolean supportsBLOB() { return true; } // supportsBLOB /** * String Representation * @return info */ public String toString() { StringBuilder sb = new StringBuilder("DB_Oracle["); sb.append(m_connectionURL); try { StringBuilder logBuffer = new StringBuilder(); HikariPoolMXBean mxBean = m_ds.getHikariPoolMXBean(); logBuffer.append("# Connections: ").append(mxBean.getTotalConnections()); logBuffer.append(" , # Busy Connections: ").append(mxBean.getActiveConnections()); logBuffer.append(" , # Idle Connections: ").append(mxBean.getIdleConnections()); logBuffer.append(" , # Threads waiting on connection: ").append(mxBean.getThreadsAwaitingConnection()); } catch (Exception e) { sb.append("=").append(e.getLocalizedMessage()); } sb.append("]"); return sb.toString(); } // toString /** * Get Status * @return status info */ public String getStatus() { if (m_ds == null) { return null; } StringBuilder sb = new StringBuilder(); try { HikariPoolMXBean mxBean = m_ds.getHikariPoolMXBean(); sb.append("# Connections: ").append(mxBean.getTotalConnections()); sb.append(" , # Busy Connections: ").append(mxBean.getActiveConnections()); sb.append(" , # Idle Connections: ").append(mxBean.getIdleConnections()); sb.append(" , # Threads waiting on connection: ").append(mxBean.getThreadsAwaitingConnection()); sb.append(" , # Min Pool Size: ").append(m_ds.getMinimumIdle()); sb.append(" , # Max Pool Size: ").append(m_ds.getMaximumPoolSize()); sb.append(" , # Open Transactions: ").append(Trx.getOpenTransactions().length); } catch (Exception e) {} return sb.toString(); } // getStatus /************************************************************************** * Convert an individual Oracle Style statements to target database statement syntax. * @param oraStatement oracle statement * @return converted Statement oracle statement */ public String convertStatement (String oraStatement) { Convert.logMigrationScript(oraStatement, null); if (SystemProperties.isDBDebug()) { String filterOrDebug = SystemProperties.getDBDebugFilter(); boolean print = true; if (filterOrDebug != null) print = oraStatement.matches(filterOrDebug); if (print) log.warning("Oracle -> " + oraStatement); } return oraStatement; } // convertStatement /** * Check if DBMS support the sql statement * @sql SQL statement * @return true: yes */ public boolean isSupported(String sql) { return true; //jz temp, modify later } /** * Get constraint type associated with the index * @tableName table name * @IXName Index name * @return String[0] = 0: do not know, 1: Primary Key 2: Foreign Key * String[1] - String[n] = Constraint Name */ public String getConstraintType(Connection conn, String tableName, String IXName) { if (IXName == null || IXName.length()==0) return "0"; if (IXName.endsWith("_KEY")) return "1"+IXName; else return "0"; //jz temp, modify later from user.constraints } /** * Get Name of System User * @return system */ public String getSystemUser() { String systemUser = SystemProperties.getAdempiereDBSystemUser(); if (systemUser == null) systemUser = "system"; return systemUser; } // getSystemUser /** * Get Name of System Database * @param databaseName database Name * @return e.g. master or database Name */ public String getSystemDatabase(String databaseName) { return databaseName; } // getSystemDatabase /** * Create SQL TO Date String from Timestamp * * @param time Date to be converted * @param dayOnly true if time set to 00:00:00 * * @return TO_DATE('2001-01-30 18:10:20',''YYYY-MM-DD HH24:MI:SS') * or TO_DATE('2001-01-30',''YYYY-MM-DD') */ public String TO_DATE (Timestamp time, boolean dayOnly) { if (time == null) { if (dayOnly) return "TRUNC(getDate())"; return "getDate()"; } StringBuilder dateString = new StringBuilder("TO_DATE('"); // YYYY-MM-DD HH24:MI:SS.mmmm JDBC Timestamp format String myDate = time.toString(); if (dayOnly) { dateString.append(myDate.substring(0,10)); dateString.append("','YYYY-MM-DD')"); } else { dateString.append(myDate.substring(0, myDate.indexOf('.'))); // cut off miliseconds dateString.append("','YYYY-MM-DD HH24:MI:SS')"); } return dateString.toString(); } // TO_DATE /** * Create SQL for formatted Date, Number * * @param columnName the column name in the SQL * @param displayType Display Type * @param AD_Language 6 character language setting (from Env.LANG_*) * * @return TRIM(TO_CHAR(columnName,'999G999G999G990D00','NLS_NUMERIC_CHARACTERS='',.''')) * or TRIM(TO_CHAR(columnName,'TM9')) depending on DisplayType and Language * @see org.compiere.util.DisplayType * @see org.compiere.util.Env * * */ public String TO_CHAR (String columnName, int displayType, String AD_Language) { StringBuilder retValue = new StringBuilder("TRIM(TO_CHAR("); retValue.append(columnName); // Numbers if (DisplayType.isNumeric(displayType)) { if (displayType == DisplayType.Amount) retValue.append(",'999G999G999G990D00'"); else retValue.append(",'TM9'"); // TO_CHAR(GrandTotal,'999G999G999G990D00','NLS_NUMERIC_CHARACTERS='',.''') if (!Language.isDecimalPoint(AD_Language)) // reversed retValue.append(",'NLS_NUMERIC_CHARACTERS='',.'''"); } else if (DisplayType.isDate(displayType)) { retValue.append(",'") .append(Language.getLanguage(AD_Language).getDBdatePattern()) .append("'"); } retValue.append("))"); // return retValue.toString(); } // TO_CHAR /** * Return number as string for INSERT statements with correct precision * @param number number * @param displayType display Type * @return number as string */ public String TO_NUMBER (BigDecimal number, int displayType) { if (number == null) return "NULL"; BigDecimal result = number; int scale = DisplayType.getDefaultPrecision(displayType); if (scale > number.scale()) { try { result = number.setScale(scale, RoundingMode.HALF_UP); } catch (Exception e) { // log.severe("Number=" + number + ", Scale=" + " - " + e.getMessage()); } } return result.toString(); } // TO_NUMBER /** * @return string with right casting for JSON inserts */ public String getJSONCast () { return "?"; } /** * Return string as JSON object for INSERT statements * @param value * @return value as json */ public String TO_JSON (String value) { return value; } /** * Get SQL Commands. * The following variables are resolved: * @SystemPassword@, @AdempiereUser@, @AdempierePassword@ * @SystemPassword@, @DatabaseName@, @DatabaseDevice@ * @param cmdType CMD_* * @return array of commands to be executed */ public String[] getCommands (int cmdType) { if (CMD_CREATE_USER == cmdType) return new String[] { }; // if (CMD_CREATE_DATABASE == cmdType) return new String[] { }; // if (CMD_DROP_DATABASE == cmdType) return new String[] { }; // return null; } // getCommands private String getPoolPropertiesFile () { String base = Ini.getAdempiereHome(); if (base != null && !base.endsWith(File.separator)) { base += File.separator; } // return base + getName() + File.separator + POOL_PROPERTIES; } // getFileName public DataSource getDataSource(CConnection connection) { ensureInitialized(connection); return m_ds; } /** * Get Cached Connection * @param connection connection * @param autoCommit auto commit * @param transactionIsolation trx isolation * @return Connection * @throws Exception */ public Connection getCachedConnection (CConnection connection, boolean autoCommit, int transactionIsolation) throws Exception { Connection conn = null; if (m_ds == null) getDataSource(connection); // If HikariCP has no available free connection this call will block until either // a connection becomes available or the configured 'connectionTimeout' value is // reached (after which a SQLException is thrown). conn = m_ds.getConnection(); if (conn.getTransactionIsolation() != transactionIsolation) { conn.setTransactionIsolation(transactionIsolation); } if (conn.getAutoCommit() != autoCommit) { conn.setAutoCommit(autoCommit); } return conn; } // getCachedConnection /** * Get Connection from Driver * @param connection info * @return connection or null * @throws SQLException */ public Connection getDriverConnection (CConnection connection) throws SQLException { getDriver(); return DriverManager.getConnection (getConnectionURL (connection), connection.getDbUid(), connection.getDbPwd()); } // getDriverConnection /** * Get Driver Connection * @param dbUrl URL * @param dbUid user * @param dbPwd password * @return connection * @throws SQLException */ public Connection getDriverConnection (String dbUrl, String dbUid, String dbPwd) throws SQLException { getDriver(); return DriverManager.getConnection (dbUrl, dbUid, dbPwd); } // getDriverConnection private Properties getPoolProperties() { //check property file from home File userPropertyFile = new File(getPoolPropertiesFile()); URL propertyFileURL = null; if (userPropertyFile.exists() && userPropertyFile.canRead()) { try { propertyFileURL = userPropertyFile.toURI().toURL(); } catch (Exception e) { e.printStackTrace(); } } if (propertyFileURL == null) { propertyFileURL = OracleBundleActivator.bundleContext.getBundle().getEntry("META-INF/pool/server.default.properties"); } Properties poolProperties = new Properties(); try (InputStream propertyFileInputStream = propertyFileURL.openStream()) { poolProperties.load(propertyFileInputStream); } catch (Exception e) { throw new DBException(e); } //auto create property file at home folder from default config if (!userPropertyFile.exists()) { try { Path directory = userPropertyFile.toPath().getParent(); Files.createDirectories(directory); try (InputStream propertyFileInputStream = propertyFileURL.openStream()) { Files.copy(propertyFileInputStream, userPropertyFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } } catch (Exception e) { e.printStackTrace(); } } return poolProperties; } /** Boolean to indicate the PostgreSQL connection pool is either initializing or initialized.*/ private final AtomicBoolean initialized = new AtomicBoolean(false); /** Latch which can be used to wait for initialization completion. */ private final CountDownLatch initializedLatch = new CountDownLatch(1); /** * Allows the connection pool to be lazily initialized. While it might be preferable to do * this once upon initialization of this class the current design of iDempiere makes this * hard. * * Calling this method will block until the pool is configured. This does NOT mean it will * block until a database connection has been setup. * * @param connection */ private void ensureInitialized(CConnection connection) { if (!initialized.compareAndSet(false, true)) { try { initializedLatch.await(); } catch (InterruptedException e) { return; } } try { Properties poolProperties = getPoolProperties(); // Do not override values which might have been read from the users // hikaricp.properties file. if(!poolProperties.containsKey("jdbcUrl")) { poolProperties.put("jdbcUrl", getConnectionURL(connection)); } if (!poolProperties.containsKey("username")) { poolProperties.put("username", connection.getDbUid()); } if (!poolProperties.containsKey("password")) { poolProperties.put("password", connection.getDbPwd()); } HikariConfig hikariConfig = new HikariConfig(poolProperties); hikariConfig.setDriverClassName(DRIVER); m_ds = new HikariDataSource(hikariConfig); m_connectionURL = m_ds.getJdbcUrl(); initializedLatch.countDown(); } catch (Exception ex) { throw new IllegalStateException("Could not initialise Hikari Datasource", ex); } } /** * Close */ public void close() { if (log.isLoggable(Level.CONFIG)) { log.config(toString()); } try { m_ds.close(); } catch (Exception e) { e.printStackTrace(); } } // close /** * Clean up */ public void cleanup() { } // cleanup /** * Get Data Type * @param columnName * @param displayType display type * @param precision precision * @param defaultValue if true adds default value * @return data type * @deprecated */ public String getDataType (String columnName, int displayType, int precision, boolean defaultValue) { return DisplayType.getSQLDataType(displayType, columnName, precision); } // getDataType /** * Check and generate an alternative SQL * @reExNo number of re-execution * @msg previous execution error message * @sql previous executed SQL * @return String, the alternative SQL, null if no alternative */ public String getAlternativeSQL(int reExNo, String msg, String sql) { //check reExNo or based on reExNo to do a decision. Currently none return null; //do not do re-execution of alternative SQL } public Convert getConvert() { return m_convert; } public int getNextID(String name) { return getNextID(name, null); } public int getNextID(String name, String trxName) { int m_sequence_id = DB.getSQLValueEx(trxName, "SELECT "+name.toUpperCase()+".nextval FROM DUAL"); return m_sequence_id; } public boolean createSequence(String name , int increment , int minvalue , int maxvalue ,int start , String trxName) { // Check if Sequence exists final int cnt = DB.getSQLValueEx(trxName, "SELECT COUNT(*) FROM USER_SEQUENCES WHERE UPPER(sequence_name)=?", name.toUpperCase()); final int no; if (start < minvalue) start = minvalue; // // New Sequence if (cnt == 0) { no = DB.executeUpdate("CREATE SEQUENCE "+name.toUpperCase() + " MINVALUE " + minvalue + " MAXVALUE " + maxvalue + " START WITH " + start + " INCREMENT BY " + increment + " CACHE 20", trxName); } // // Already existing sequence => ALTER else { no = DB.executeUpdate("ALTER SEQUENCE "+name.toUpperCase() + " INCREMENT BY " + increment // + " MINVALUE " + minvalue // ORA-04007 + " MAXVALUE " + maxvalue + " CACHE 20", trxName); while (DB.getSQLValue(trxName, "SELECT " + name.toUpperCase() + ".NEXTVAL FROM DUAL") < start) { // do nothing - the while is incrementing the sequence } } if(no == -1 ) return false; else return true; } public boolean isQueryTimeoutSupported() { return true; } /** * Implemented using the fetch first and offset feature. use 1 base index for start and end parameter * @param sql * @param start * @param end */ public String addPagingSQL(String sql, int start, int end) { StringBuilder newSql = new StringBuilder(sql); if (start > 1) { newSql.append(" OFFSET ") .append((start - 1)) .append( " ROWS"); } if (end > 0) { newSql.append(" FETCH FIRST ") .append(( end - start + 1 )) .append(" ROWS ONLY"); } return newSql.toString(); } public boolean isPagingSupported() { return true; } @Override public boolean forUpdate(PO po, int timeout) { //only can lock for update if using trx if (po.get_TrxName() == null) return false; String[] keyColumns = po.get_KeyColumns(); if (keyColumns != null && keyColumns.length > 0 && !po.is_new()) { StringBuilder sqlBuffer = new StringBuilder(" SELECT "); sqlBuffer.append(keyColumns[0]) .append(" FROM ") .append(po.get_TableName()) .append(" WHERE "); for(int i = 0; i < keyColumns.length; i++) { if (i > 0) sqlBuffer.append(" AND "); sqlBuffer.append(keyColumns[i]).append("=?"); } sqlBuffer.append(" FOR UPDATE WAIT ").append((timeout > 0 ? timeout : LOCK_TIME_OUT)); Object[] parameters = new Object[keyColumns.length]; for(int i = 0; i < keyColumns.length; i++) { Object parameter = po.get_Value(keyColumns[i]); if (parameter != null && parameter instanceof Boolean) { if ((Boolean) parameter) parameter = "Y"; else parameter = "N"; } parameters[i] = parameter; } PreparedStatement stmt = null; ResultSet rs = null; try { stmt = DB.prepareStatement(sqlBuffer.toString(), ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE, po.get_TrxName()); for(int i = 0; i < keyColumns.length; i++) { stmt.setObject(i+1, parameters[i]); } rs = stmt.executeQuery(); if (rs.next()) { return true; } else { return false; } } catch (Exception e) { if (log.isLoggable(Level.INFO))log.log(Level.INFO, e.getLocalizedMessage(), e); throw new DBException("Could not lock record for " + po.toString() + " caused by " + e.getLocalizedMessage(), e); } finally { DB.close(rs, stmt); } } return false; } @Override public String getNameOfUniqueConstraintError(Exception e) { String info = e.getMessage(); int fromIndex = info.indexOf("."); if (fromIndex == -1) return info; int toIndex = info.indexOf(")", fromIndex + 1); if (toIndex == -1) return info; return info.substring(fromIndex + 1, toIndex); } @Override public String getForeignKeyConstraint(Exception e) { // finding the name of foreign key constraint is the same as unique constraint return getNameOfUniqueConstraintError(e); } @Override public String subsetClauseForCSV(String columnName, String csv) { StringBuilder builder = new StringBuilder(); builder.append("toTableOfVarchar2(") .append(columnName) .append(")"); builder.append(" submultiset of ") .append("toTableOfVarchar2(") .append(DB.TO_STRING(csv)) .append(")"); return builder.toString(); } @Override public String intersectClauseForCSV(String columnName, String csv) { return intersectClauseForCSV(columnName, csv, false); } @Override public String intersectClauseForCSV(String columnName, String csv, boolean isNotClause) { StringBuilder builder = new StringBuilder(); builder.append("toTableOfVarchar2(") .append(columnName) .append(")"); builder.append(" MULTISET INTERSECT ") .append("toTableOfVarchar2(") .append(DB.TO_STRING(csv)).append(") IS "); if(!isNotClause) builder.append("NOT "); builder.append("EMPTY"); return builder.toString(); } @Override public String getNumericDataType() { return "NUMBER"; } @Override public String getCharacterDataType() { return "CHAR"; } @Override public String getVarcharDataType() { return "VARCHAR2"; } /** * @return variable length character data type suffix */ public String getVarcharLengthSuffix() { return " CHAR"; }; @Override public String getBlobDataType() { return "BLOB"; } @Override public String getClobDataType() { return "CLOB"; } @Override public String getJsonDataType() { return getClobDataType(); } @Override public String getTimestampDataType() { return "DATE"; } @Override public String getTimestampWithTimezoneDataType() { return "TIMESTAMP WITH TIME ZONE"; } @Override public String getSQLDDL(MColumn column) { StringBuilder sql = new StringBuilder ().append(column.getColumnName()) .append(" ").append(column.getSQLDataType()); // Default String defaultValue = column.getDefaultValue(); if (defaultValue != null && defaultValue.length() > 0 && defaultValue.indexOf('@') == -1 // no variables && ( ! (DisplayType.isID(column.getAD_Reference_ID()) && defaultValue.equals("-1") ) ) ) // not for ID's with default -1 { if (DisplayType.isText(column.getAD_Reference_ID()) || DisplayType.isList(column.getAD_Reference_ID()) || column.getAD_Reference_ID() == DisplayType.YesNo // Two special columns: Defined as Table but DB Type is String || column.getColumnName().equals("EntityType") || column.getColumnName().equals("AD_Language") || (column.getAD_Reference_ID() == DisplayType.Button && !(column.getColumnName().endsWith("_ID")))) { if (!defaultValue.startsWith("'") && !defaultValue.endsWith("'")) defaultValue = DB.TO_STRING(defaultValue); } sql.append(" DEFAULT ").append(defaultValue); } else { if (! column.isMandatory()) sql.append(" DEFAULT NULL "); defaultValue = null; } // Inline Constraint if (column.getAD_Reference_ID() == DisplayType.YesNo) sql.append(" CHECK (").append(column.getColumnName()).append(" IN ('Y','N'))"); else if (column.getAD_Reference_ID() == DisplayType.JSON) sql.append(" CONSTRAINT ").append(column.getAD_Table().getTableName()).append("_").append(column.getColumnName()).append("_isjson CHECK (").append(column.getColumnName()).append(" IS JSON)"); // Null if (column.isMandatory()) sql.append(" NOT NULL"); return sql.toString(); } /** * Get SQL Add command * @param table table * @return sql */ @Override public String getSQLAdd (MTable table, MColumn column) { StringBuilder sql = new StringBuilder ("ALTER TABLE ") .append(table.getTableName()) .append(" ADD ").append(column.getSQLDDL()); String constraint = column.getConstraint(table.getTableName()); if (constraint != null && constraint.length() > 0) { sql.append(DB.SQLSTATEMENT_SEPARATOR).append("ALTER TABLE ") .append(table.getTableName()) .append(" ADD ").append(constraint); } return sql.toString(); } // getSQLAdd /** * Get SQL Modify command * @param table table * @param setNullOption generate null / not null statement * @return sql separated by ; */ public String getSQLModify (MTable table, MColumn column, boolean setNullOption) { StringBuilder sql = new StringBuilder(); StringBuilder sqlBase = new StringBuilder ("ALTER TABLE ") .append(table.getTableName()) .append(" MODIFY ").append(column.getColumnName()); // Default StringBuilder sqlDefault = new StringBuilder(sqlBase) .append(" ").append(column.getSQLDataType()); String defaultValue = column.getDefaultValue(); String originalDefaultValue = defaultValue; if (defaultValue != null && defaultValue.length() > 0 && defaultValue.indexOf('@') == -1 // no variables && ( ! (DisplayType.isID(column.getAD_Reference_ID()) && defaultValue.equals("-1") ) ) ) // not for ID's with default -1 { if (DisplayType.isText(column.getAD_Reference_ID()) || DisplayType.isList(column.getAD_Reference_ID()) || column.getAD_Reference_ID() == DisplayType.YesNo || column.getAD_Reference_ID() == DisplayType.Payment // Two special columns: Defined as Table but DB Type is String || column.getColumnName().equals("EntityType") || column.getColumnName().equals("AD_Language") || (column.getAD_Reference_ID() == DisplayType.Button && !(column.getColumnName().endsWith("_ID")))) { if (!defaultValue.startsWith("'") && !defaultValue.endsWith("'")) defaultValue = DB.TO_STRING(defaultValue); } sqlDefault.append(" DEFAULT ").append(defaultValue); } else { if (! column.isMandatory()) sqlDefault.append(" DEFAULT NULL "); defaultValue = null; } sql.append(sqlDefault); // Constraint if (column.getAD_Reference_ID() == DisplayType.JSON) sql.append(" CONSTRAINT ").append(column.getAD_Table().getTableName()).append("_").append(column.getColumnName()).append("_isjson CHECK (").append(column.getColumnName()).append(" IS JSON)"); // Null Values if (column.isMandatory() && defaultValue != null && defaultValue.length() > 0) { if (!(DisplayType.isText(column.getAD_Reference_ID()) || DisplayType.isList(column.getAD_Reference_ID()) || column.getAD_Reference_ID() == DisplayType.YesNo || column.getAD_Reference_ID() == DisplayType.Payment // Two special columns: Defined as Table but DB Type is String || column.getColumnName().equals("EntityType") || column.getColumnName().equals("AD_Language") || (column.getAD_Reference_ID() == DisplayType.Button && !(column.getColumnName().endsWith("_ID"))))) { defaultValue = originalDefaultValue; } StringBuilder sqlSet = new StringBuilder("UPDATE ") .append(table.getTableName()) .append(" SET ").append(column.getColumnName()) .append("=").append(defaultValue) .append(" WHERE ").append(column.getColumnName()).append(" IS NULL"); sql.append(DB.SQLSTATEMENT_SEPARATOR).append(sqlSet); } // Null if (setNullOption) { StringBuilder sqlNull = new StringBuilder(sqlBase); if (column.isMandatory()) sqlNull.append(" NOT NULL"); else sqlNull.append(" NULL"); sql.append(DB.SQLSTATEMENT_SEPARATOR).append(sqlNull); } // return sql.toString(); } // getSQLModify @Override public boolean isQueryTimeout(SQLException ex) { //java.sql.SQLTimeoutException: ORA-01013: user requested cancel of current operation return "72000".equals(ex.getSQLState()) && ex.getErrorCode() == 1013; } @Override public ITablePartitionService getTablePartitionService() { return new TablePartitionService(); } } // DB_Oracle