/****************************************************************************** * 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.util; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.math.BigDecimal; import java.sql.CallableStatement; import java.sql.Clob; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.Statement; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Properties; import java.util.logging.Level; import javax.sql.RowSet; import org.adempiere.exceptions.AdempiereException; import org.adempiere.exceptions.DBException; import org.adempiere.util.ProcessUtil; import org.compiere.Adempiere; import org.compiere.db.AdempiereDatabase; import org.compiere.db.CConnection; import org.compiere.db.Database; import org.compiere.db.ProxyFactory; import org.compiere.model.MAcctSchema; import org.compiere.model.MLanguage; import org.compiere.model.MRole; import org.compiere.model.MSequence; import org.compiere.model.MSysConfig; import org.compiere.model.MSystem; import org.compiere.model.MTable; import org.compiere.model.PO; import org.compiere.model.POResultSet; import org.compiere.model.SystemIDs; import org.compiere.process.ProcessInfo; import org.compiere.process.ProcessInfoParameter; /** * Static methods for JDBC interface * * @author Jorg Janke * @version $Id: DB.java,v 1.8 2006/10/09 00:22:29 jjanke Exp $ * --- * @author Ashley Ramdass (Posterita) *
  • Modifications: removed static references to database connection and instead always * get a new connection from database pool manager which manages all connections * set rw/ro properties for the connection accordingly. * * @author Teo Sarca, SC ARHIPAC SERVICE SRL *
  • BF [ 1647864 ] WAN: delete record error *
  • FR [ 1884435 ] Add more DB.getSQLValue helper methods *
  • FR [ 1904460 ] DB.executeUpdate should handle Boolean params *
  • BF [ 1962568 ] DB.executeUpdate should handle null params *
  • FR [ 1984268 ] DB.executeUpdateEx should throw DBException *
  • FR [ 1986583 ] Add DB.executeUpdateEx(String, Object[], String) *
  • BF [ 2030233 ] Remove duplicate code from DB class *
  • FR [ 2107062 ] Add more DB.getKeyNamePairs methods *
  • FR [ 2448461 ] Introduce DB.getSQLValue*Ex methods *
  • FR [ 2781053 ] Introduce DB.getValueNamePairs *
  • FR [ 2818480 ] Introduce DB.createT_Selection helper method * https://sourceforge.net/p/adempiere/feature-requests/757/ * @author Teo Sarca, teo.sarca@gmail.com *
  • BF [ 2873324 ] DB.TO_NUMBER should be a static method * https://sourceforge.net/p/adempiere/bugs/2160/ *
  • FR [ 2873891 ] DB.getKeyNamePairs should use trxName * https://sourceforge.net/p/adempiere/feature-requests/847/ * @author Paul Bowden, phib BF 2900767 Zoom to child tab - inefficient queries * @see https://sourceforge.net/p/adempiere/bugs/2222/ */ public final class DB { /** Connection Descriptor */ private static CConnection s_cc = null; /** Logger */ private static CLogger log = CLogger.getCLogger (DB.class); /** Lock object for mutual access to {@link #s_cc} */ private static Object s_ccLock = new Object(); /** SQL Statement Separator "; " */ public static final String SQLSTATEMENT_SEPARATOR = "; "; /** * Check need for post Upgrade * @param ctx context * @return true if post upgrade ran - false if there was no need */ @Deprecated(forRemoval = true, since = "11") public static boolean afterMigration (Properties ctx) { // UPDATE AD_System SET IsJustMigrated='Y' MSystem system = MSystem.get(ctx); if (!system.isJustMigrated()) return false; // Role update log.info("Role"); String sql = "SELECT * FROM AD_Role"; PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement (sql, null); rs = pstmt.executeQuery (); while (rs.next ()) { MRole role = new MRole (ctx, rs, null); role.updateAccessRecords(); } } catch (Exception e) { log.log(Level.SEVERE, "(1)", e); } finally { close(rs); close(pstmt); rs= null; pstmt = null; } // Release Specif stuff & Print Format try { Class clazz = Class.forName("org.compiere.MigrateData"); clazz.getDeclaredConstructor().newInstance(); } catch (Exception e) { log.log (Level.SEVERE, "Data", e); } // Language check log.info("Language"); MLanguage.maintain(ctx); // Sequence check log.info("Sequence"); ProcessInfo processInfo = new ProcessInfo("Sequence Check", 0); processInfo.setClassName("org.compiere.process.SequenceCheck"); processInfo.setParameter(new ProcessInfoParameter[0]); ProcessUtil.startJavaProcess(ctx, processInfo, null); // Costing Setup log.info("Costing"); MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(ctx, 0); for (int i = 0; i < ass.length; i++) { ass[i].checkCosting(); ass[i].saveEx(); } // Reset Flag system.setIsJustMigrated(false); return system.save(); } // afterMigration /** * Update Mail Settings for System Client and System User (idempiereEnv.properties) */ public static void updateMail() { // Get Property File String envName = Ini.getAdempiereHome(); if (envName == null) return; envName += File.separator + "idempiereEnv.properties"; File envFile = new File(envName); if (!envFile.exists()) return; Properties env = new Properties(); try { FileInputStream in = new FileInputStream(envFile); env.load(in); in.close(); } catch (Exception e) { return; } String updated = env.getProperty("ADEMPIERE_MAIL_UPDATED"); if (updated != null && updated.equals("Y")) return; // See org.compiere.install.ConfigurationData String server = env.getProperty("ADEMPIERE_MAIL_SERVER"); if (server == null || server.length() == 0) return; String adminEMail = env.getProperty("ADEMPIERE_ADMIN_EMAIL"); if (adminEMail == null || adminEMail.length() == 0) return; String mailUser = env.getProperty("ADEMPIERE_MAIL_USER"); if (mailUser == null || mailUser.length() == 0) return; String mailPassword; if (!env.containsKey("ADEMPIERE_MAIL_PASSWORD") && MSystem.isSecureProps()) mailPassword = Ini.getVar("ADEMPIERE_MAIL_PASSWORD"); else mailPassword = env.getProperty("ADEMPIERE_MAIL_PASSWORD"); // StringBuilder sql = new StringBuilder("UPDATE AD_Client SET") .append(" SMTPHost=").append(DB.TO_STRING(server)) .append(", RequestEMail=").append(DB.TO_STRING(adminEMail)) .append(", RequestUser=").append(DB.TO_STRING(mailUser)) .append(", RequestUserPW=").append(DB.TO_STRING(mailPassword)) .append(", IsSMTPAuthorization='Y' WHERE AD_Client_ID=0"); int no = DB.executeUpdate(sql.toString(), null); if (log.isLoggable(Level.FINE)) log.fine("Client #"+no); // sql = new StringBuilder("UPDATE AD_User SET ") .append(" EMail=").append(DB.TO_STRING(adminEMail)) .append(", EMailUser=").append(DB.TO_STRING(mailUser)) .append(", EMailUserPW=").append(DB.TO_STRING(mailPassword)) .append(" WHERE AD_User_ID IN (?,?,?)"); no = DB.executeUpdate(sql.toString(), new Object[]{SystemIDs.USER_SYSTEM_DEPRECATED, SystemIDs.USER_SYSTEM, SystemIDs.USER_SUPERUSER}, false, null); if (log.isLoggable(Level.FINE)) log.fine("User #"+no); // try (FileOutputStream out = new FileOutputStream(envFile)) { env.setProperty("ADEMPIERE_MAIL_UPDATED", "Y"); env.store(out, ""); out.flush(); } catch (Exception e) { } } // updateMail /** * Set active connection profile * @param cc connection profile */ public synchronized static void setDBTarget (CConnection cc) { if (cc == null) throw new IllegalArgumentException("Connection is NULL"); if (s_cc != null && s_cc.equals(cc)) return; DB.closeTarget(); // synchronized(s_ccLock) { s_cc = cc; } s_cc.setDataSource(); if (log.isLoggable(Level.CONFIG)) log.config(s_cc + " - DS=" + s_cc.isDataSource()); } // setDBTarget /** * Connect to database and initialise all connections. * @return True if success, false otherwise */ @Deprecated public static boolean connect() { //direct connection boolean success =false; try { Connection conn = getConnection(); if (conn != null) { s_cc.readInfo(conn); conn.close(); } success = (conn != null); } catch (Exception e) { //logging here could cause infinite loop //log.log(Level.SEVERE, "Could not connect to DB", e); System.err.println("Could not connect to DB - " + e.getLocalizedMessage()); e.printStackTrace(); success = false; } return success; } /** * Is connected to DB. * @return true, if connected to database */ public static boolean isConnected() { //bug [1637432] if (s_cc == null) return false; //get connection boolean success = false; try { Connection conn = getConnection(); // try to get a connection if (conn != null) { conn.close(); success = true; } else success = false; } catch (Exception e) { success = false; } return success; } /** * Replace by {@link #isConnected()} * * Is there a connection to the database ? * @param createNew ignore * @return true, if connected to database * @deprecated */ @Deprecated (since="10", forRemoval=true) public static boolean isConnected(boolean createNew) { return isConnected(); } // isConnected /** * Get auto commit connection from connection pool. * @return {@link Connection} */ public static Connection getConnection() { return getConnection(true); } /** * Get auto or not auto commit connection from connection pool.
    * Usually, developer should use @{@link #getConnection()} instead to get auto commit connection * and use {@link Trx} to works with not autoCommit connection. * @param autoCommit * @return {@link Connection} */ public static Connection getConnection(boolean autoCommit) { return createConnection(autoCommit, Connection.TRANSACTION_READ_COMMITTED); } /** * Replace by @{@link #getConnection()} * * @return Connection (r/w) * @deprecated */ @Deprecated (since="10", forRemoval=true) public static Connection getConnectionRW() { return getConnection(); } /** * Replace by @{@link #getConnection()} * * Return (pooled) r/w AutoCommit, Serializable connection. * For Transaction control use Trx.getConnection() * @param createNew ignore * @return Connection (r/w) * @deprecated */ @Deprecated (since="10", forRemoval=true) public static Connection getConnectionRW (boolean createNew) { return getConnection(); } // getConnectionRW /** * Replace by @{@link #getConnection(boolean)}. * Note that this is intended for internal use only from the beginning. * * Return everytime a new r/w no AutoCommit, Serializable connection. * To be used to ID * @return Connection (r/w) * @deprecated */ @Deprecated (since="10", forRemoval=true) public static Connection getConnectionID () { return getConnection(false); } // getConnectionID /** * Replace by @{@link #getConnection()}. Use {@link Trx} instead for readonly transaction. * * Return read committed, read/only from pool. * @return Connection (r/o) * @deprecated */ @Deprecated (since="10", forRemoval=true) public static Connection getConnectionRO () { return getConnection(); } // getConnectionRO /** * Return a replica connection if possible, otherwise from pool. * @return Connection (r/o) */ public static Connection getReportingConnectionRO () { Connection conn = DBReadReplica.getConnectionRO(); if (conn == null) conn = getConnection(); return conn; } // getReportingConnectionRO /** * Create new Connection.
    * The connection must be closed explicitly by the caller.
    * Usually, developer should not call this directly. * * @param autoCommit auto commit * @param trxLevel - Connection.TRANSACTION_READ_UNCOMMITTED, Connection.TRANSACTION_READ_COMMITTED, Connection.TRANSACTION_REPEATABLE_READ, or Connection.TRANSACTION_READ_COMMITTED. * @return Connection connection */ public static Connection createConnection (boolean autoCommit, int trxLevel) { Connection conn = s_cc.getConnection (autoCommit, trxLevel); if (conn == null) { throw new IllegalStateException("DB.createConnection - @NoDBConnection@"); } //hengsin: failed to set autocommit can lead to severe lock up of the system try { if (conn != null && conn.getAutoCommit() != autoCommit) { throw new IllegalStateException("Failed to set the requested auto commit mode on connection. [autoCommit=" + autoCommit +"]"); } } catch (SQLException e) {} return conn; } // createConnection /** * Replace by {@link #createConnection(boolean, int)}. * Use {@link Trx} instead for readonly transaction. * * Create new Connection. * The connection must be closed explicitly by the application. * * @param autoCommit auto commit * @param readOnly ignore * @param trxLevel - Connection.TRANSACTION_READ_UNCOMMITTED, Connection.TRANSACTION_READ_COMMITTED, Connection.TRANSACTION_REPEATABLE_READ, or Connection.TRANSACTION_READ_COMMITTED. * @return Connection connection * @deprecated */ @Deprecated (since="10", forRemoval=true) public static Connection createConnection (boolean autoCommit, boolean readOnly, int trxLevel) { return createConnection(autoCommit, trxLevel); } // createConnection /** * Get Database Adapter.
    * Access to database specific functionality. * @return iDempiere Database Adapter */ public static AdempiereDatabase getDatabase() { if (s_cc != null) return s_cc.getDatabase(); log.severe("No Database Connection"); return null; } // getDatabase /** * Get Database Adapter.
    * Access to database specific functionality. * @param URL JDBC connection url * @return iDempiere Database Adapter */ public static AdempiereDatabase getDatabase(String URL) { return Database.getDatabaseFromURL(URL); } // getDatabase /** * Is connected to Oracle DB ? * @return true if connected to Oracle */ public static boolean isOracle() { if (s_cc != null) return s_cc.isOracle(); log.severe("No Database Connection"); return false; } // isOracle /** * Is connected to PostgreSQL DB ? * @return true if connected to PostgreSQL */ public static boolean isPostgreSQL() { if (s_cc != null) return s_cc.isPostgreSQL(); log.severe("No Database"); return false; } // isPostgreSQL /** * Get Database Info * @return info */ public static String getDatabaseInfo() { if (s_cc != null) return s_cc.getDBInfo(); return "No Database"; } // getDatabaseInfo /** * Check database Version with Code version * @param ctx context * @return true if Database version (date) is the same * @deprecated */ @Deprecated (since="10", forRemoval=true) public static boolean isDatabaseOK (Properties ctx) { // Check Version String version = "?"; String sql = "SELECT Version FROM AD_System"; PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = prepareStatement(sql, null); rs = pstmt.executeQuery(); if (rs.next()) version = rs.getString(1); } catch (SQLException e) { log.log(Level.SEVERE, "Problem with AD_System Table - Run system.sql script - " + e.toString()); return false; } finally { close(rs); close(pstmt); rs= null; pstmt = null; } if (log.isLoggable(Level.INFO)) log.info("DB_Version=" + version); // Identical DB version if (Adempiere.DB_VERSION.equals(version)) return true; String AD_Message = "DatabaseVersionError"; // Code assumes Database version {0}, but Database has Version {1}. String msg = Msg.getMsg(ctx, AD_Message, new Object[] {Adempiere.DB_VERSION, version}); // complete message System.err.println(msg); return false; } // isDatabaseOK /** * Check Build Version of Database against running client * @param ctx context * @return true if Database version (date) is the same */ public static boolean isBuildOK (Properties ctx) { // Check Build String buildClient = Adempiere.getVersion(); String buildDatabase = ""; boolean failOnBuild = false; String sql = "SELECT LastBuildInfo, IsFailOnBuildDiffer FROM AD_System"; PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = prepareStatement(sql, null); rs = pstmt.executeQuery(); if (rs.next()) { buildDatabase = rs.getString(1); failOnBuild = rs.getString(2).equals("Y"); } } catch (SQLException e) { log.log(Level.SEVERE, "Problem with AD_System Table - Run system.sql script - " + e.toString()); return false; } finally { close(rs); close(pstmt); rs= null; pstmt = null; } if (log.isLoggable(Level.INFO)){ log.info("Build DB=" + buildDatabase); log.info("Build Cl=" + buildClient); } // Identical DB version if (buildClient.equals(buildDatabase)) return true; String AD_Message = "BuildVersionError"; // The program assumes build version {0}, but database has build Version {1}. String msg = Msg.getMsg(ctx, AD_Message, new Object[] {buildClient, buildDatabase}); // complete message if (! failOnBuild) { log.warning(msg); return true; } log.log(Level.SEVERE, msg); return false; } // isDatabaseOK /** * Close DB connection profile */ public static void closeTarget() { boolean closed = false; // CConnection if (s_cc != null) { closed = true; s_cc.setDataSource(null); } s_cc = null; if (closed) log.fine("closed"); } // closeTarget /** * Create callable statement proxy * @param sql * @return Callable Statement */ public static CallableStatement prepareCall(String sql) { return prepareCall(sql, ResultSet.CONCUR_UPDATABLE, null); } /** * Create callable statement proxy * @param SQL * @param resultSetConcurrency * @param trxName * @return Callable Statement */ public static CallableStatement prepareCall(String SQL, int resultSetConcurrency, String trxName) { if (SQL == null || SQL.length() == 0) throw new IllegalArgumentException("Required parameter missing - " + SQL); return ProxyFactory.newCCallableStatement(ResultSet.TYPE_FORWARD_ONLY, resultSetConcurrency, SQL, trxName); } // prepareCall /** * Prepare Statement * @param sql * @return Prepared Statement * @deprecated */ @Deprecated public static CPreparedStatement prepareStatement (String sql) { return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, null); } // prepareStatement /** * Create prepare Statement proxy * @param sql * @param trxName transaction * @return Prepared Statement */ public static CPreparedStatement prepareStatement (String sql, String trxName) { return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, trxName); } // prepareStatement /** * Create prepare Statement proxy * @param connection * @param sql * @return Prepared Statement */ public static CPreparedStatement prepareStatement (Connection connection, String sql) { return prepareStatement(connection, sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); } // prepareStatement /** * Prepare Statement. * @param sql * @param resultSetType - ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.TYPE_SCROLL_SENSITIVE * @param resultSetConcurrency - ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE * @return Prepared Statement * @deprecated */ @Deprecated public static CPreparedStatement prepareStatement (String sql, int resultSetType, int resultSetConcurrency) { return prepareStatement(sql, resultSetType, resultSetConcurrency, null); } // prepareStatement /** * Create prepare Statement proxy * @param sql * @param resultSetType - ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.TYPE_SCROLL_SENSITIVE * @param resultSetConcurrency - ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE * @param trxName transaction name * @return Prepared Statement */ public static CPreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, String trxName) { if (sql == null || sql.length() == 0) throw new IllegalArgumentException("No SQL"); // return ProxyFactory.newCPreparedStatement(resultSetType, resultSetConcurrency, sql, trxName); } // prepareStatement /** * Create prepare Statement proxy * @param connection * @param sql sql statement * @param resultSetType - ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.TYPE_SCROLL_SENSITIVE * @param resultSetConcurrency - ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE * @return Prepared Statement r/o or r/w depending on concur */ public static CPreparedStatement prepareStatement(Connection connection, String sql, int resultSetType, int resultSetConcurrency) { if (sql == null || sql.length() == 0) throw new IllegalArgumentException("No SQL"); // return ProxyFactory.newCPreparedStatement(resultSetType, resultSetConcurrency, sql, connection); } // prepareStatement /** * Create Statement proxy * @return Statement */ public static Statement createStatement() { return createStatement (ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, null); } // createStatement /** * Create Statement Proxy. * @param resultSetType - ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.TYPE_SCROLL_SENSITIVE * @param resultSetConcurrency - ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE * @param trxName transaction name * @return Statement */ public static Statement createStatement(int resultSetType, int resultSetConcurrency, String trxName) { return ProxyFactory.newCStatement(resultSetType, resultSetConcurrency, trxName); } // createStatement /** * Set parameters for given statement * @param stmt statements * @param params parameters array; if null or empty array, no parameters are set */ public static void setParameters(PreparedStatement stmt, Object[] params) throws SQLException { if (params == null || params.length == 0) { return; } // for (int i = 0; i < params.length; i++) { setParameter(stmt, i+1, params[i]); } } /** * Set parameters for given statement * @param stmt statements * @param params parameters list; if null or empty list, no parameters are set */ public static void setParameters(PreparedStatement stmt, List params) throws SQLException { if (params == null || params.size() == 0) { return; } for (int i = 0; i < params.size(); i++) { setParameter(stmt, i+1, params.get(i)); } } /** * Set PreparedStatement's parameter.
    * Similar with calling pstmt.setObject(index, param) * @param pstmt * @param index * @param param * @throws SQLException */ public static void setParameter(PreparedStatement pstmt, int index, Object param) throws SQLException { if (param == null) pstmt.setObject(index, null); else if (param instanceof String) pstmt.setString(index, (String)param); else if (param instanceof Integer) pstmt.setInt(index, ((Integer)param).intValue()); else if (param instanceof BigDecimal) pstmt.setBigDecimal(index, (BigDecimal)param); else if (param instanceof Timestamp) pstmt.setTimestamp(index, (Timestamp)param); else if (param instanceof Boolean) pstmt.setString(index, ((Boolean)param).booleanValue() ? "Y" : "N"); else if (param instanceof byte[]) pstmt.setBytes(index, (byte[]) param); else if (param instanceof Clob) pstmt.setClob(index, (Clob) param); else if (param.getClass().getName().equals("oracle.sql.BLOB")) pstmt.setObject(index, param); else throw new DBException("Unknown parameter type "+index+" - "+param); } /** * Execute Update. * saves "DBExecuteError" in Log * @param sql * @return number of rows updated or -1 if error * @deprecated */ @Deprecated public static int executeUpdate (String sql) { return executeUpdate(sql, null, false, null); } // executeUpdate /** * Execute Update.
    * Saves "DBExecuteError" in Log.
    * Developer is recommended to call {@link #executeUpdateEx(String, String)} instead. * @param sql * @param trxName optional transaction name * @return number of rows updated or -1 if error */ public static int executeUpdate (String sql, String trxName) { return executeUpdate(sql, trxName, 0); } // executeUpdate /** * Execute Update.
    * Saves "DBExecuteError" in Log.
    * Developer is recommended to call {@link #executeUpdateEx(String, String, int)} instead. * @param sql * @param trxName optional transaction name * @param timeOut optional timeout parameter * @return number of rows updated or -1 if error */ public static int executeUpdate (String sql, String trxName, int timeOut) { return executeUpdate(sql, null, false, trxName, timeOut); } // executeUpdate /** * Execute Update. * saves "DBExecuteError" in Log * @param sql * @param ignoreError if true, no execution error is reported * @return number of rows updated or -1 if error * @deprecated */ @Deprecated public static int executeUpdate (String sql, boolean ignoreError) { return executeUpdate (sql, null, ignoreError, null); } // executeUpdate /** * Execute Update.
    * Saves "DBExecuteError" in Log.
    * Developer is recommended to call {@link #executeUpdateEx(String, String)} instead. * @param sql * @param ignoreError if true, no execution error is reported * @param trxName transaction * @return number of rows updated or -1 if error */ public static int executeUpdate (String sql, boolean ignoreError, String trxName) { return executeUpdate (sql, ignoreError, trxName, 0); } // executeUpdate /** * Execute Update.
    * Saves "DBExecuteError" in Log.
    * Developer is recommended to call {@link #executeUpdateEx(String, String, int)} instead. * @param sql * @param ignoreError if true, no execution error is reported * @param trxName transaction * @param timeOut optional timeOut parameter * @return number of rows updated or -1 if error */ public static int executeUpdate (String sql, boolean ignoreError, String trxName, int timeOut) { return executeUpdate (sql, null, ignoreError, trxName, timeOut); } /** * Execute Update.
    * Saves "DBExecuteError" in Log.
    * Developer is recommended to call {@link #executeUpdateEx(String, Object[], String)} instead. * @param sql * @param param int param * @param trxName transaction * @return number of rows updated or -1 if error */ public static int executeUpdate (String sql, int param, String trxName) { return executeUpdate (sql, param, trxName, 0); } // executeUpdate /** * Execute Update.
    * Saves "DBExecuteError" in Log.
    * Developer is recommended to call {@link #executeUpdateEx(String, Object[], String, int)} instead. * @param sql * @param param int param * @param trxName transaction * @param timeOut optional timeOut parameter * @return number of rows updated or -1 if error */ public static int executeUpdate (String sql, int param, String trxName, int timeOut) { return executeUpdate (sql, new Object[]{Integer.valueOf(param)}, false, trxName, timeOut); } // executeUpdate /** * Execute Update.
    * Saves "DBExecuteError" in Log.
    * Developer is recommended to call {@link #executeUpdateEx(String, Object[], String)} instead. * @param sql * @param param int parameter * @param ignoreError if true, no execution error is reported * @param trxName transaction * @return number of rows updated or -1 if error */ public static int executeUpdate (String sql, int param, boolean ignoreError, String trxName) { return executeUpdate (sql, param, ignoreError, trxName, 0); } // executeUpdate /** * Execute Update.
    * Saves "DBExecuteError" in Log. * Developer is recommended to call {@link #executeUpdateEx(String, Object[], String, int)} instead. * @param sql * @param param int parameter * @param ignoreError if true, no execution error is reported * @param trxName transaction * @param timeOut optional timeOut parameter * @return number of rows updated or -1 if error */ public static int executeUpdate (String sql, int param, boolean ignoreError, String trxName, int timeOut) { return executeUpdate (sql, new Object[]{Integer.valueOf(param)}, ignoreError, trxName, timeOut); } // executeUpdate /** * Execute Update.
    * Saves "DBExecuteError" in Log.
    * Developer is recommended to call {@link #executeUpdateEx(String, Object[], String)} instead. * @param sql * @param params array of parameters * @param ignoreError if true, no execution error is reported * @param trxName optional transaction name * @return number of rows updated or -1 if error */ public static int executeUpdate (String sql, Object[] params, boolean ignoreError, String trxName) { return executeUpdate(sql, params, ignoreError, trxName, 0); } /** * Execute Update.
    * Saves "DBExecuteError" in Log.
    * Developer is recommended to call {@link #executeUpdateEx(String, Object[], String, int)} instead. * @param sql * @param params array of parameters * @param ignoreError if true, no execution error is reported * @param trxName optional transaction name * @param timeOut optional timeOut parameter * @return number of rows updated or -1 if error */ public static int executeUpdate (String sql, Object[] params, boolean ignoreError, String trxName, int timeOut) { if (sql == null || sql.length() == 0) throw new IllegalArgumentException("Required parameter missing - " + sql); verifyTrx(trxName); // int no = -1; CPreparedStatement cs = ProxyFactory.newCPreparedStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE, sql, trxName); // converted in call try { setParameters(cs, params); //set timeout if (timeOut > 0) { cs.setQueryTimeout(timeOut); } no = cs.executeUpdate(); } catch (Exception e) { e = getSQLException(e); if (ignoreError) log.log(Level.SEVERE, cs.getSql() + " [" + trxName + "] - " + e.getMessage()); else { log.log(Level.SEVERE, cs.getSql() + " [" + trxName + "]", e); String msg = DBException.getDefaultDBExceptionMessage(e); log.saveError (msg != null ? msg : "DBExecuteError", e); } } finally { // Always close cursor close(cs); cs = null; } return no; } // executeUpdate /** * Execute update and throw DBException if there are errors. * @param sql * @param params statement parameters * @param trxName transaction * @return number of rows updated * @throws SQLException */ public static int executeUpdateEx (String sql, Object[] params, String trxName) throws DBException { return executeUpdateEx(sql, params, trxName, 0); } /** * Execute update and throw DBException if there are errors. * @param sql * @param params statement parameters * @param trxName transaction * @param timeOut optional timeOut parameter * @return number of rows updated * @throws DBException */ public static int executeUpdateEx (String sql, Object[] params, String trxName, int timeOut) throws DBException { if (sql == null || sql.length() == 0) throw new IllegalArgumentException("Required parameter missing - " + sql); // verifyTrx(trxName); int no = -1; CPreparedStatement cs = ProxyFactory.newCPreparedStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE, sql, trxName); // converted in call try { setParameters(cs, params); if (timeOut > 0) { { cs.setQueryTimeout(timeOut); } } no = cs.executeUpdate(); } catch (Exception e) { throw new DBException(e); } finally { close(cs); cs = null; } return no; } /** * Execute multiple Update statements.
    * Saves (last) "DBExecuteError" in Log. * @param sql multiple sql statements separated by "; " SQLSTATEMENT_SEPARATOR * @param ignoreError if true, no execution error is reported * @param trxName optional transaction name * @return number of rows updated or -1 if error */ public static int executeUpdateMultiple (String sql, boolean ignoreError, String trxName) { if (sql == null || sql.length() == 0) throw new IllegalArgumentException("Required parameter missing - " + sql); int index = sql.indexOf(SQLSTATEMENT_SEPARATOR); if (index == -1) return executeUpdate(sql, null, ignoreError, trxName); int no = 0; // String statements[] = sql.split(SQLSTATEMENT_SEPARATOR); for (int i = 0; i < statements.length; i++) { if (log.isLoggable(Level.FINE)) log.fine(statements[i]); no += executeUpdate(statements[i], null, ignoreError, trxName); } return no; } // executeUpdareMultiple /** * Execute update and throw DBException if there are errors. * @param sql * @param trxName * @see {@link #executeUpdateEx(String, Object[], String)} */ public static int executeUpdateEx (String sql, String trxName) throws DBException { return executeUpdateEx(sql, trxName, 0); } // executeUpdateEx /** * Execute update and throw DBException if there are errors. * @param sql * @param trxName * @param timeOut * @see {@link #executeUpdateEx(String, Object[], String)} */ public static int executeUpdateEx (String sql, String trxName, int timeOut) throws DBException { return executeUpdateEx(sql, null, trxName, timeOut); } // executeUpdateEx /** * Commit transaction * @param throwException if true, re-throws exception * @param trxName transaction name * @return true if not needed (trxName is null) or success * @throws SQLException */ public static boolean commit (boolean throwException, String trxName) throws SQLException,IllegalStateException { // Not on transaction scope, Connection are thus auto commit if (trxName == null) { return true; } try { Trx trx = Trx.get(trxName, false); if (trx != null) return trx.commit(true); if (throwException) { throw new IllegalStateException("Could not load transation with identifier: " + trxName); } else { return false; } } catch (SQLException e) { log.log(Level.SEVERE, "[" + trxName + "]", e); if (throwException) throw e; return false; } } // commit /** * Rollback transaction * @param throwException if true, re-throws exception * @param trxName transaction name * @return true if not needed (trxName is null) or success * @throws SQLException */ public static boolean rollback (boolean throwException, String trxName) throws SQLException { // Not on transaction scope, Connection are thus auto commit/rollback if (trxName == null) { return true; } try { Trx trx = Trx.get(trxName, false); if (trx != null) return trx.rollback(true); if (throwException) { throw new IllegalStateException("Could not load transation with identifier: " + trxName); } else { return false; } } catch (SQLException e) { log.log(Level.SEVERE, "[" + trxName + "]", e); if (throwException) throw e; return false; } } // commit /** * Get Row Set.
    * When a Rowset is closed, it also closes the underlying connection. * @param sql * @return row set or null */ public static RowSet getRowSet (String sql) { CStatementVO info = new CStatementVO (RowSet.TYPE_SCROLL_INSENSITIVE, RowSet.CONCUR_READ_ONLY, DB.getDatabase().convertStatement(sql)); CPreparedStatement stmt = null; RowSet retValue = null; try { stmt = ProxyFactory.newCPreparedStatement(info); retValue = stmt.getRowSet(); } finally { close(stmt); } return retValue; } // getRowSet /** * Get int Value from sql * @param trxName optional transaction name * @param sql * @param params array of parameters * @return first value or -1 if not found * @throws DBException if there is any SQLException */ public static int getSQLValueEx (String trxName, String sql, Object... params) throws DBException { int retValue = -1; PreparedStatement pstmt = null; ResultSet rs = null; Connection conn = null; if (trxName == null) conn = DB.createConnection(true, Connection.TRANSACTION_READ_COMMITTED); try { if (conn != null) { conn.setAutoCommit(false); conn.setReadOnly(true); } if (conn != null) pstmt = prepareStatement(conn, sql); else pstmt = prepareStatement(sql, trxName); setParameters(pstmt, params); rs = pstmt.executeQuery(); if (rs.next()) retValue = rs.getInt(1); else if (log.isLoggable(Level.FINE)) log.fine("No Value " + sql); } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } throw new DBException(e, sql); } finally { close(rs, pstmt); rs = null; pstmt = null; if (conn != null) { closeAndResetReadonlyConnection(conn); } } return retValue; } /** * Reset connection's auto commit to true and read only to false before closing it. * @param conn */ private static void closeAndResetReadonlyConnection(Connection conn) { try { conn.setAutoCommit(true); } catch (SQLException e) { e.printStackTrace(); } try { conn.setReadOnly(false); } catch (SQLException e) { e.printStackTrace(); } try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } /** * Get int value from sql * @param trxName optional transaction name * @param sql * @param params collection of parameters * @return first value or -1 * @throws DBException if there is any SQLException */ public static int getSQLValueEx (String trxName, String sql, List params) { return getSQLValueEx(trxName, sql, params.toArray(new Object[params.size()])); } /** * Get int Value from sql.
    * Developer is recommended to call {@link #getSQLValueEx(String, String, Object...)} instead. * @param trxName optional transaction name * @param sql * @param params array of parameters * @return first value or -1 if not found or error */ public static int getSQLValue (String trxName, String sql, Object... params) { int retValue = -1; try { retValue = getSQLValueEx(trxName, sql, params); } catch (Exception e) { log.log(Level.SEVERE, sql, getSQLException(e)); } return retValue; } /** * Get int value from sql.
    * Developer is recommended to call {@link #getSQLValueEx(String, String, List)} instead. * @param trxName optional transaction name * @param sql * @param params collection of parameters * @return first value or null */ public static int getSQLValue (String trxName, String sql, List params) { return getSQLValue(trxName, sql, params.toArray(new Object[params.size()])); } /** * Get string value from sql * @param trxName optional transaction name * @param sql * @param params array of parameters * @return first value or null * @throws DBException if there is any SQLException */ public static String getSQLValueStringEx (String trxName, String sql, Object... params) { String retValue = null; PreparedStatement pstmt = null; ResultSet rs = null; Connection conn = null; if (trxName == null) conn = DB.createConnection(true, Connection.TRANSACTION_READ_COMMITTED); try { if (conn != null) { conn.setAutoCommit(false); conn.setReadOnly(true); } if (conn != null) pstmt = prepareStatement(conn, sql); else pstmt = prepareStatement(sql, trxName); setParameters(pstmt, params); rs = pstmt.executeQuery(); if (rs.next()) retValue = rs.getString(1); else if (log.isLoggable(Level.FINE)) log.fine("No Value " + sql); } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } throw new DBException(e, sql); } finally { close(rs, pstmt); rs = null; pstmt = null; if (conn != null) { closeAndResetReadonlyConnection(conn); } } return retValue; } /** * Get String Value from sql * @param trxName optional transaction name * @param sql * @param params collection of parameters * @return first value or null * @throws DBException if there is any SQLException */ public static String getSQLValueStringEx (String trxName, String sql, List params) { return getSQLValueStringEx(trxName, sql, params.toArray(new Object[params.size()])); } /** * Get String Value from sql * @param trxName optional transaction name * @param sql * @param params array of parameters * @return first value or null */ public static String getSQLValueString (String trxName, String sql, Object... params) { String retValue = null; try { retValue = getSQLValueStringEx(trxName, sql, params); } catch (Exception e) { log.log(Level.SEVERE, sql, getSQLException(e)); } return retValue; } /** * Get string value from sql.
    * Developer is recommended to call {@link #getSQLValueStringEx(String, String, List)} instead. * @param trxName optional transaction name * @param sql * @param params collection of parameters * @return first value or null */ public static String getSQLValueString (String trxName, String sql, List params) { return getSQLValueString(trxName, sql, params.toArray(new Object[params.size()])); } /** * Get BigDecimal value from sql * @param trxName optional transaction name * @param sql * @param params array of parameters * @return first value or null if not found * @throws DBException if there is any SQLException */ public static BigDecimal getSQLValueBDEx (String trxName, String sql, Object... params) throws DBException { BigDecimal retValue = null; PreparedStatement pstmt = null; ResultSet rs = null; Connection conn = null; if (trxName == null) conn = DB.createConnection(true, Connection.TRANSACTION_READ_COMMITTED); try { if (conn != null) { conn.setAutoCommit(false); conn.setReadOnly(true); } if (conn != null) pstmt = prepareStatement(conn, sql); else pstmt = prepareStatement(sql, trxName); setParameters(pstmt, params); rs = pstmt.executeQuery(); if (rs.next()) retValue = rs.getBigDecimal(1); else if (log.isLoggable(Level.FINE)) log.fine("No Value " + sql); } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } throw new DBException(e, sql); } finally { close(rs, pstmt); rs = null; pstmt = null; if (conn != null) { closeAndResetReadonlyConnection(conn); } } return retValue; } /** * Get BigDecimal Value from sql * @param trxName optional transaction name * @param sql * @param params collection of parameters * @return first value or null if not found * @throws DBException if there is any SQLException */ public static BigDecimal getSQLValueBDEx (String trxName, String sql, List params) throws DBException { return getSQLValueBDEx(trxName, sql, params.toArray(new Object[params.size()])); } /** * Get BigDecimal Value from sql.
    * Developer is recommended to call {@link #getSQLValueBDEx(String, String, Object...)} instead. * @param trxName optional transaction name * @param sql * @param params array of parameters * @return first value or null */ public static BigDecimal getSQLValueBD (String trxName, String sql, Object... params) { try { return getSQLValueBDEx(trxName, sql, params); } catch (Exception e) { log.log(Level.SEVERE, sql, getSQLException(e)); } return null; } /** * Get BigDecimal Value from sql.
    * Developer is recommended to call {@link #getSQLValueBDEx(String, String, List)} instead. * @param trxName optional transaction name * @param sql * @param params collection of parameters * @return first value or null */ public static BigDecimal getSQLValueBD (String trxName, String sql, List params) { return getSQLValueBD(trxName, sql, params.toArray(new Object[params.size()])); } /** * Get Timestamp Value from sql * @param trxName optional transaction name * @param sql * @param params array of parameters * @return first value or null * @throws DBException if there is any SQLException */ public static Timestamp getSQLValueTSEx (String trxName, String sql, Object... params) { Timestamp retValue = null; PreparedStatement pstmt = null; ResultSet rs = null; Connection conn = null; if (trxName == null) conn = DB.createConnection(true, Connection.TRANSACTION_READ_COMMITTED); try { if (conn != null) { conn.setAutoCommit(false); conn.setReadOnly(true); } if (conn != null) pstmt = prepareStatement(conn, sql); else pstmt = prepareStatement(sql, trxName); setParameters(pstmt, params); rs = pstmt.executeQuery(); if (rs.next()) retValue = rs.getTimestamp(1); else if (log.isLoggable(Level.FINE)) log.fine("No Value " + sql); } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } throw new DBException(e, sql); } finally { close(rs, pstmt); rs = null; pstmt = null; if (conn != null) { closeAndResetReadonlyConnection(conn); } } return retValue; } /** * Get Timestamp Value from sql * @param trxName optional transaction name * @param sql * @param params collection of parameters * @return first value or null if not found * @throws DBException if there is any SQLException */ public static Timestamp getSQLValueTSEx (String trxName, String sql, List params) throws DBException { return getSQLValueTSEx(trxName, sql, params.toArray(new Object[params.size()])); } /** * Get Timestamp Value from sql.
    * Developer is recommended to call {@link #getSQLValueTSEx(String, String, Object...)} instead. * @param trxName optional transaction name * @param sql * @param params array of parameters * @return first value or null */ public static Timestamp getSQLValueTS (String trxName, String sql, Object... params) { try { return getSQLValueTSEx(trxName, sql, params); } catch (Exception e) { log.log(Level.SEVERE, sql, getSQLException(e)); } return null; } /** * Get Timestamp Value from sql.
    * Developer is recommended to call {@link #getSQLValueTSEx(String, String, List)} instead. * @param trxName optional transaction name * @param sql * @param params collection of parameters * @return first value or null */ public static Timestamp getSQLValueTS (String trxName, String sql, List params) { Object[] arr = new Object[params.size()]; params.toArray(arr); return getSQLValueTS(trxName, sql, arr); } /** * Get Array of Key Name Pairs * @param sql select with id / name as first / second column * @param optional if true (-1,"") is added * @return array of {@link KeyNamePair} * @see #getKeyNamePairs(String, boolean, Object...) */ public static KeyNamePair[] getKeyNamePairs(String sql, boolean optional) { return getKeyNamePairs(sql, optional, (Object[])null); } /** * Get Array of Key Name Pairs * @param sql select with id / name as first / second column * @param optional if true (-1,"") is added * @return array of {@link KeyNamePair} * @see #getKeyNamePairs(String, boolean, Object...) */ public static KeyNamePair[] getKeyNamePairsEx(String sql, boolean optional) { return getKeyNamePairsEx(sql, optional, (Object[])null); } /** * Get Array of Key Name Pairs * @param sql select with id / name as first / second column * @param optional if true (-1,"") is added * @param params query parameters */ public static KeyNamePair[] getKeyNamePairs(String sql, boolean optional, Object ... params) { return getKeyNamePairs(null, sql, optional, params); } /** * Get Array of Key Name Pairs * @param sql select with id / name as first / second column * @param optional if true (-1,"") is added * @param params query parameters */ public static KeyNamePair[] getKeyNamePairsEx(String sql, boolean optional, Object ... params) { return getKeyNamePairsEx(null, sql, optional, params); } /** * Get Array of Key Name Pairs * @param trxName * @param sql select with id / name as first / second column * @param optional if true (-1,"") is added * @param params query parameters * @return Array of Key Name Pairs */ public static KeyNamePair[] getKeyNamePairs(String trxName, String sql, boolean optional, Object ... params) { try { return getKeyNamePairsEx(trxName, sql, optional, params); } catch (Exception e) { log.log(Level.SEVERE, sql, getSQLException(e)); } return new KeyNamePair[0]; } /** * Get Array of Key Name Pairs * @param trxName * @param sql select with id / name as first / second column * @param optional if true (-1,"") is added * @param params query parameters * @return Array of Key Name Pairs */ public static KeyNamePair[] getKeyNamePairsEx(String trxName, String sql, boolean optional, Object ... params) { PreparedStatement pstmt = null; ResultSet rs = null; Connection conn = null; if (trxName == null) conn = DB.createConnection(true, Connection.TRANSACTION_READ_COMMITTED); ArrayList list = new ArrayList(); if (optional) { list.add (new KeyNamePair(-1, "")); } try { if (conn != null) { conn.setAutoCommit(false); conn.setReadOnly(true); } if (conn != null) pstmt = prepareStatement(conn, sql); else pstmt = DB.prepareStatement(sql, trxName); setParameters(pstmt, params); rs = pstmt.executeQuery(); while (rs.next()) { list.add(new KeyNamePair(rs.getInt(1), rs.getString(2))); } } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } throw new DBException(e.getMessage(), e); } finally { close(rs, pstmt); rs= null; pstmt = null; if (conn != null) { closeAndResetReadonlyConnection(conn); } } KeyNamePair[] retValue = new KeyNamePair[list.size()]; list.toArray(retValue); return retValue; } // getKeyNamePairs /** * Get Array of IDs * @param trxName * @param sql select with id as first column * @param params query parameters * @throws DBException if there is any SQLException */ public static int[] getIDsEx(String trxName, String sql, Object ... params) throws DBException { PreparedStatement pstmt = null; ResultSet rs = null; ArrayList list = new ArrayList(); try { pstmt = DB.prepareStatement(sql, trxName); setParameters(pstmt, params); rs = pstmt.executeQuery(); while (rs.next()) { list.add(rs.getInt(1)); } } catch (SQLException e) { throw new DBException(e, sql); } finally { close(rs, pstmt); rs= null; pstmt = null; } // Convert to array int[] retValue = new int[list.size()]; for (int i = 0; i < retValue.length; i++) { retValue[i] = list.get(i); } return retValue; } // getIDsEx /** * Is Sales Order Trx.
    * Assumes Sales Order. Query IsSOTrx value of table with where clause * @param TableName table * @param whereClause where clause * @param windowNo * @return true (default) or false if tested that not SO */ public static boolean isSOTrx (String TableName, String whereClause, int windowNo) { if (TableName == null || TableName.length() == 0) { log.severe("No TableName"); return true; } if (whereClause == null || whereClause.length() == 0) { log.severe("No Where Clause"); return true; } // Boolean isSOTrx = null; boolean noIsSOTrxColumn = false; if (MTable.get(Env.getCtx(), TableName).getColumn("IsSOTrx") == null) { noIsSOTrxColumn = true; } else { String sql = "SELECT IsSOTrx FROM " + TableName + " WHERE " + whereClause; PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement (sql, null); rs = pstmt.executeQuery (); if (rs.next ()) isSOTrx = Boolean.valueOf("Y".equals(rs.getString(1))); } catch (Exception e) { noIsSOTrxColumn = true; } finally { close(rs, pstmt); rs= null; pstmt = null; } } if (noIsSOTrxColumn && TableName.endsWith("Line")) { noIsSOTrxColumn = false; String hdr = TableName.substring(0, TableName.indexOf("Line")); if (MTable.get(Env.getCtx(), hdr) == null || MTable.get(Env.getCtx(), hdr).getColumn("IsSOTrx") == null) { noIsSOTrxColumn = true; } else { // use IN instead of EXISTS as the subquery should be highly selective String sql = "SELECT IsSOTrx FROM " + hdr + " h WHERE h." + hdr + "_ID IN (SELECT l." + hdr + "_ID FROM " + TableName + " l WHERE " + whereClause + ")"; PreparedStatement pstmt2 = null; ResultSet rs2 = null; try { pstmt2 = DB.prepareStatement (sql, null); rs2 = pstmt2.executeQuery (); if (rs2.next ()) isSOTrx = Boolean.valueOf("Y".equals(rs2.getString(1))); } catch (Exception ee) { noIsSOTrxColumn = true; } finally { close(rs2, pstmt2); rs2= null; pstmt2 = null; } } } if (noIsSOTrxColumn) if (log.isLoggable(Level.FINEST))log.log(Level.FINEST, TableName + " - No SOTrx"); if (isSOTrx == null) { if (windowNo >= 0) { // check context isSOTrx = Boolean.valueOf("Y".equals(Env.getContext(Env.getCtx(), windowNo, "IsSOTrx"))); } else { isSOTrx = Boolean.TRUE; } } return isSOTrx.booleanValue(); } // isSOTrx /** * Delegate to {@link #isSOTrx(String, String, int)} with -1 for windowNo parameter. * @param TableName * @param whereClause * @return true (default) or false if tested that not SO */ public static boolean isSOTrx (String TableName, String whereClause) { return isSOTrx (TableName, whereClause, -1); } /** * Get next id for table * @param ctx client * @param TableName table name * @param trxName optional transaction name * @return next id no */ public static int getNextID (Properties ctx, String TableName, String trxName) { if (ctx == null) throw new IllegalArgumentException("Context missing"); if (TableName == null || TableName.length() == 0) throw new IllegalArgumentException("TableName missing"); return getNextID(Env.getAD_Client_ID(ctx), TableName, trxName); } // getNextID /** * Get next id for table * @param AD_Client_ID client * @param TableName table name * @param trxName optional Transaction Name * @return next id no * @see {@link MSequence#getNextID(int, String, String)} */ public static int getNextID (int AD_Client_ID, String TableName, String trxName) { return MSequence.getNextID (AD_Client_ID, TableName, trxName); } // getNextID /** * Get Document No based on Document Type (backward compatibility) * @param C_DocType_ID document type * @param trxName optional Transaction Name * @return document no or null * @deprecated */ @Deprecated public static String getDocumentNo(int C_DocType_ID, String trxName) { return MSequence.getDocumentNo (C_DocType_ID, trxName, false); } // getDocumentNo /** * Get Document No based on Document Type * @param C_DocType_ID document type * @param trxName optional Transaction Name * @param definite asking for a definitive or temporary sequence * @return document no or null */ public static String getDocumentNo(int C_DocType_ID, String trxName, boolean definite) { return getDocumentNo(C_DocType_ID, trxName, definite, null); } /** * Get Document No based on Document Type * @param C_DocType_ID document type * @param trxName optional Transaction Name * @param definite asking for a definitive or temporary sequence * @param po PO * @return document no or null * @see {@link MSequence#getDocumentNo(int, String, boolean, PO)} */ public static String getDocumentNo(int C_DocType_ID, String trxName, boolean definite, PO po) { return MSequence.getDocumentNo (C_DocType_ID, trxName, definite, po); } // getDocumentNo /** * Get Document No for table * @param AD_Client_ID client * @param TableName table name * @param trxName optional Transaction Name * @return document no or null */ public static String getDocumentNo (int AD_Client_ID, String TableName, String trxName) { return getDocumentNo(AD_Client_ID, TableName, trxName, null); } /** * Get Document No for table * @param AD_Client_ID client * @param TableName table name * @param trxName optional Transaction Name * @param po * @return document no or null * @see {@link MSequence#getDocumentNo(int, String, String, PO)} */ public static String getDocumentNo (int AD_Client_ID, String TableName, String trxName, PO po) { String dn = MSequence.getDocumentNo (AD_Client_ID, TableName, trxName, po); if (dn == null) throw new DBException ("No DocumentNo"); return dn; } // getDocumentNo /** * Get Document Number for current document. *
    * - first search for DocumentNo based on DocType from environment context
    * - then search for DocumentNo based on TableName * @param ctx context * @param WindowNo window * @param TableName table * @param onlyDocType Do not search for document no based on TableName * @param trxName optional Transaction Name * @return DocumentNo or null, if no doc number defined */ public static String getDocumentNo (Properties ctx, int WindowNo, String TableName, boolean onlyDocType, String trxName) { if (ctx == null || TableName == null || TableName.length() == 0) throw new IllegalArgumentException("Required parameter missing"); int AD_Client_ID = Env.getContextAsInt(ctx, WindowNo, "AD_Client_ID"); // Get C_DocType_ID from context - NO Defaults - int C_DocType_ID = Env.getContextAsInt(ctx, WindowNo + "|C_DocTypeTarget_ID"); if (C_DocType_ID == 0) C_DocType_ID = Env.getContextAsInt(ctx, WindowNo + "|C_DocType_ID"); if (C_DocType_ID == 0) { if (log.isLoggable(Level.FINE)) log.fine("Window=" + WindowNo + " - Target=" + Env.getContextAsInt(ctx, WindowNo + "|C_DocTypeTarget_ID") + "/" + Env.getContextAsInt(ctx, WindowNo, "C_DocTypeTarget_ID") + " - Actual=" + Env.getContextAsInt(ctx, WindowNo + "|C_DocType_ID") + "/" + Env.getContextAsInt(ctx, WindowNo, "C_DocType_ID")); return getDocumentNo (AD_Client_ID, TableName, trxName); } String retValue = getDocumentNo (C_DocType_ID, trxName, false); if (!onlyDocType && retValue == null) return getDocumentNo (AD_Client_ID, TableName, trxName); return retValue; } // getDocumentNo /** * Is this a remote client connection. * * Deprecated, always return false. * @return true if client and RMI or Objects on Server * @deprecated */ @Deprecated (forRemoval=true) public static boolean isRemoteObjects() { return false; } // isRemoteObjects /** * Is this a remote client connection * * Deprecated, always return false. * @return true if client and RMI or Process on Server * @deprecated */ @Deprecated (forRemoval=true) public static boolean isRemoteProcess() { return false; } // isRemoteProcess /** * Print SQL Warnings. *
    * Usage: DB.printWarning("comment", rs.getWarnings()); * @param comment comment * @param warning warning */ public static void printWarning (String comment, SQLWarning warning) { if (comment == null || warning == null || comment.length() == 0) return; log.warning(comment); // SQLWarning warn = warning; while (warn != null) { StringBuilder buffer = new StringBuilder(); buffer.append(warn.getMessage()) .append("; State=").append(warn.getSQLState()) .append("; ErrorCode=").append(warn.getErrorCode()); log.warning(buffer.toString()); warn = warn.getNextWarning(); } } // printWarning /** * 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 static String TO_DATE (Timestamp time, boolean dayOnly) { return s_cc.getDatabase().TO_DATE(time, dayOnly); } // TO_DATE /** * Create SQL TO Date String from Timestamp * @param day day time * @return TO_DATE String (day only) */ public static String TO_DATE (Timestamp day) { return TO_DATE(day, true); } // 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 static String TO_CHAR (String columnName, int displayType, String AD_Language) { if (columnName == null || AD_Language == null || columnName.length() == 0) throw new IllegalArgumentException("Required parameter missing"); return s_cc.getDatabase().TO_CHAR(columnName, displayType, AD_Language); } // TO_CHAR /** * Return number as string for INSERT statements with correct precision * @param number number * @param displayType display Type * @return number as string */ public static String TO_NUMBER (BigDecimal number, int displayType) { return s_cc.getDatabase().TO_NUMBER(number, displayType); } // TO_NUMBER /** * Package Strings for SQL command in quotes * @param txt String with text * @return escaped string for sql statement (NULL if null) */ public static String TO_STRING (String txt) { return TO_STRING (txt, 0); } // TO_STRING /** * Package Strings for SQL command in quotes. *
    	 *	    -	include in ' (single quotes)
    	 *	    -	replace ' with ''
    	 *  
    * @param txt String with text * @param maxLength Maximum Length of content or 0 to ignore * @return escaped string for sql statement (NULL if null) */ public static String TO_STRING (String txt, int maxLength) { if (txt == null || txt.length() == 0) return "NULL"; // Length String text = txt; if (maxLength != 0 && text.length() > maxLength) text = txt.substring(0, maxLength); // copy characters (we need to look through anyway) StringBuilder out = new StringBuilder(); out.append(QUOTE); // ' for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); if (c == QUOTE) out.append("''"); else out.append(c); } out.append(QUOTE); // ' // return out.toString(); } // TO_STRING /** * Return string as JSON object for INSERT statements with correct precision * @param value * @return value as json */ public static String TO_JSON (String value) { return s_cc.getDatabase().TO_JSON(value); } /** * @return string with right casting for JSON inserts */ public static String getJSONCast() { return s_cc.getDatabase().getJSONCast(); } /** * Convenient method to close result set * @param rs */ public static void close( ResultSet rs) { try { if (rs!=null) rs.close(); } catch (SQLException e) { ; } } /** * Convenient method to close statement * @param st */ public static void close( Statement st) { try { if (st!=null) st.close(); } catch (SQLException e) { ; } } /** * Convenient method to close result set and statement * @param rs result set * @param st statement * @see #close(ResultSet) * @see #close(Statement) */ public static void close(ResultSet rs, Statement st) { close(rs); close(st); } /** * Convenient method to close a {@link POResultSet} * @param rs result set * @see POResultSet#close() */ public static void close(POResultSet rs) { if (rs != null) rs.close(); } /** * Try to get the SQLException from Exception * @param e Exception * @return SQLException if found or provided exception elsewhere */ public static Exception getSQLException(Exception e) { Throwable e1 = e; while (e1 != null) { if (e1 instanceof SQLException) return (SQLException)e1; e1 = e1.getCause(); } return e; } /** Quote */ private static final char QUOTE = '\''; // Following methods are kept for BeanShell compatibility. // See BF [ 2030233 ] Remove duplicate code from DB class // TODO: remove this when BeanShell will support varargs methods public static int getSQLValue (String trxName, String sql) { return getSQLValue(trxName, sql, new Object[]{}); } public static int getSQLValue (String trxName, String sql, int int_param1) { return getSQLValue(trxName, sql, new Object[]{int_param1}); } public static int getSQLValue (String trxName, String sql, int int_param1, int int_param2) { return getSQLValue(trxName, sql, new Object[]{int_param1, int_param2}); } public static int getSQLValue (String trxName, String sql, String str_param1) { return getSQLValue(trxName, sql, new Object[]{str_param1}); } public static int getSQLValue (String trxName, String sql, int int_param1, String str_param2) { return getSQLValue(trxName, sql, new Object[]{int_param1, str_param2}); } public static String getSQLValueString (String trxName, String sql, int int_param1) { return getSQLValueString(trxName, sql, new Object[]{int_param1}); } public static BigDecimal getSQLValueBD (String trxName, String sql, int int_param1) { return getSQLValueBD(trxName, sql, new Object[]{int_param1}); } //End BeanShell compatibility. /** * Get Array of ValueNamePair items. *
     Example:
    	 * String sql = "SELECT Name, Description FROM AD_Ref_List WHERE AD_Reference_ID=?";
    	 * ValueNamePair[] list = DB.getValueNamePairs(sql, false, params);
    	 * 
    * @param sql SELECT Value_Column, Name_Column FROM ... * @param optional if {@link ValueNamePair#EMPTY} is added * @param params query parameters * @return array of {@link ValueNamePair} or empty array * @throws DBException if there is any SQLException */ public static ValueNamePair[] getValueNamePairs(String sql, boolean optional, List params) { PreparedStatement pstmt = null; ResultSet rs = null; ArrayList list = new ArrayList(); if (optional) { list.add (ValueNamePair.EMPTY); } try { pstmt = DB.prepareStatement(sql, null); setParameters(pstmt, params); rs = pstmt.executeQuery(); while (rs.next()) { list.add(new ValueNamePair(rs.getString(1), rs.getString(2))); } } catch (SQLException e) { throw new DBException(e, sql); } finally { close(rs, pstmt); rs = null; pstmt = null; } return list.toArray(new ValueNamePair[list.size()]); } /** * Get Array of KeyNamePair items. *
     Example:
    	 * String sql = "SELECT C_City_ID, Name FROM C_City WHERE C_City_ID=?";
    	 * KeyNamePair[] list = DB.getKeyNamePairs(sql, false, params);
    	 * 
    * @param sql SELECT ID_Column, Name_Column FROM ... * @param optional if {@link ValueNamePair#EMPTY} is added * @param params query parameters * @return array of {@link KeyNamePair} or empty array * @throws DBException if there is any SQLException */ public static KeyNamePair[] getKeyNamePairs(String sql, boolean optional, List params) { PreparedStatement pstmt = null; ResultSet rs = null; ArrayList list = new ArrayList(); if (optional) { list.add (KeyNamePair.EMPTY); } try { pstmt = DB.prepareStatement(sql, null); setParameters(pstmt, params); rs = pstmt.executeQuery(); while (rs.next()) { list.add(new KeyNamePair(rs.getInt(1), rs.getString(2))); } } catch (SQLException e) { throw new DBException(e, sql); } finally { close(rs, pstmt); rs = null; pstmt = null; } return list.toArray(new KeyNamePair[list.size()]); } /** * Insert selection into T_Selection table.
    * Keeping this method for backward compatibility. * refer: IDEMPIERE-1970 * @param AD_PInstance_ID * @param selection * @param trxName */ public static void createT_Selection(int AD_PInstance_ID, Collection selection, String trxName) { StringBuilder insert = new StringBuilder(); insert.append("INSERT INTO T_SELECTION(AD_PINSTANCE_ID, T_SELECTION_ID) "); int counter = 0; for(Integer selectedId : selection) { counter++; if (counter > 1) insert.append(" UNION "); insert.append("SELECT "); insert.append(AD_PInstance_ID); insert.append(", "); insert.append(selectedId); insert.append(" FROM DUAL "); if (counter >= 1000) { DB.executeUpdateEx(insert.toString(), trxName); insert = new StringBuilder(); insert.append("INSERT INTO T_SELECTION(AD_PINSTANCE_ID, T_SELECTION_ID) "); counter = 0; } } if (counter > 0) { DB.executeUpdateEx(insert.toString(), trxName); } } /** * Insert selection into T_Selection table.
    * saveKeys is map with rowID as key and list of viewID as value. * @param AD_PInstance_ID * @param saveKeys - Collection of KeyNamePair * @param trxName */ public static void createT_SelectionNew (int AD_PInstance_ID, Collection saveKeys, String trxName) { Collection saveKeysNP = new ArrayList(); for (NamePair saveKey : saveKeys) saveKeysNP.add(saveKey); createT_SelectionNewNP(AD_PInstance_ID, saveKeysNP, trxName); } /** * Insert selection into T_Selection table.
    * saveKeys is map with rowID as key and list of viewID as value. * @param AD_PInstance_ID * @param saveKeys can receive a Collection of KeyNamePair (IDs) or ValueNamePair (UUIDs) * @param trxName */ public static void createT_SelectionNewNP (int AD_PInstance_ID, Collection saveKeys, String trxName) { String initialInsert = "INSERT INTO T_SELECTION(AD_PINSTANCE_ID, T_SELECTION_ID, T_SELECTION_UU, ViewID) "; StringBuilder insert = new StringBuilder(initialInsert); int counter = 0; for(NamePair saveKey : saveKeys) { Object selectedId; if (saveKey instanceof KeyNamePair) selectedId = ((KeyNamePair)saveKey).getKey(); else if (saveKey instanceof ValueNamePair) selectedId = ((ValueNamePair)saveKey).getValue(); else throw new AdempiereException("NamePair type not allowed in DB.createT_SelectionNewNP, just KeyNamePair or ValueNamePair are allowed"); counter++; if (counter > 1) insert.append(" UNION "); insert.append("SELECT "); insert.append(AD_PInstance_ID); insert.append(", "); if (selectedId instanceof Integer) { insert.append((Integer)selectedId); insert.append(", ' '"); } else { insert.append("0, "); insert.append(DB.TO_STRING(selectedId.toString())); } insert.append(", "); String viewIDValue = saveKey.getName(); // when no process have viewID or this process have no viewID or value of viewID is null if (viewIDValue == null){ insert.append("NULL"); }else{ insert.append(DB.TO_STRING(viewIDValue)); } insert.append(" FROM DUAL "); if (counter >= 1000) { DB.executeUpdateEx(insert.toString(), trxName); insert.delete(0, insert.length()); insert.append(initialInsert); counter = 0; } } if (counter > 0) { DB.executeUpdateEx(insert.toString(), trxName); } } private static boolean m_isUUIDVerified = false; private static boolean m_isUUIDSupported = false; /** * Is DB support generate_uuid function * @return true if current db have working generate_uuid function. generate_uuid doesn't work on 64 bit postgresql * on windows yet. */ public static boolean isGenerateUUIDSupported() { if (! m_isUUIDVerified) { String uuidTest = null; try { uuidTest = getSQLValueStringEx(null, "SELECT Generate_UUID() FROM Dual"); } catch (Exception e) {} m_isUUIDSupported = uuidTest != null && uuidTest.trim().length() == 36; m_isUUIDVerified = true; } return m_isUUIDSupported; } /** * Throw DBException if trxName doesn't return an existing Trx instance. * @param trxName */ private static void verifyTrx(String trxName) { if (trxName != null && Trx.get(trxName, false) == null) { // Using a trx that was previously closed or never opened // probably timed out - throw Exception (IDEMPIERE-644) String msg = "Transaction closed or never opened ("+trxName+") => (maybe timed out)"; log.severe(msg); // severe throw new DBException(msg); } } /** * Is table or view exists * @param tableName * @return true if table or view with name=tableName exists in db */ public static boolean isTableOrViewExists(String tableName) { Connection conn = getConnection(); ResultSet rs = null; try { DatabaseMetaData metadata = conn.getMetaData(); String tblName; if (metadata.storesUpperCaseIdentifiers()) tblName = tableName.toUpperCase(); else if (metadata.storesLowerCaseIdentifiers()) tblName = tableName.toLowerCase(); else tblName = tableName; rs = metadata.getTables(null, null, tblName, null); if (rs.next()) { return true; } } catch (SQLException e) { e.printStackTrace(); } finally { DB.close(rs); try { conn.close(); } catch (SQLException e) {} } return false; } /** * Get a list of objects from sql (one per each column in the select clause), column indexing starts with 0 * @param trxName optional transaction name * @param sql * @param params array of parameters * @return null if not found * @throws DBException if there is any SQLException */ public static List getSQLValueObjectsEx(String trxName, String sql, Object... params) { List retValue = new ArrayList(); PreparedStatement pstmt = null; ResultSet rs = null; Connection conn = null; if (trxName == null) conn = DB.createConnection(true, Connection.TRANSACTION_READ_COMMITTED); try { if (conn != null) { conn.setAutoCommit(false); conn.setReadOnly(true); } if (conn != null) pstmt = prepareStatement(conn, sql); else pstmt = prepareStatement(sql, trxName); setParameters(pstmt, params); rs = pstmt.executeQuery(); ResultSetMetaData rsmd = rs.getMetaData(); if (rs.next()) { for (int i=1; i<=rsmd.getColumnCount(); i++) { Object obj = rs.getObject(i); if (rs.wasNull()) retValue.add(null); else retValue.add(obj); } } else { retValue = null; } } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } throw new DBException(e, sql); } finally { close(rs, pstmt); rs = null; pstmt = null; if (conn != null) { closeAndResetReadonlyConnection(conn); } } return retValue; } /** * Get a list of object list from sql (one object list per each row, and in the object list, one object per each column in the select clause), * column indexing starts with 0.
    * WARNING: This method must be used just for queries returning few records, using it for many records implies heavy memory consumption * @param trxName optional transaction name * @param sql * @param params array of parameters * @return null if not found * @throws DBException if there is any SQLException */ public static List> getSQLArrayObjectsEx(String trxName, String sql, Object... params) { List> rowsArray = new ArrayList>(); PreparedStatement pstmt = null; ResultSet rs = null; Connection conn = null; if (trxName == null) conn = DB.createConnection(true, Connection.TRANSACTION_READ_COMMITTED); try { if (conn != null) { conn.setAutoCommit(false); conn.setReadOnly(true); } if (conn != null) pstmt = prepareStatement(conn, sql); else pstmt = prepareStatement(sql, trxName); setParameters(pstmt, params); rs = pstmt.executeQuery(); ResultSetMetaData rsmd = rs.getMetaData(); while (rs.next()) { List retValue = new ArrayList(); for (int i=1; i<=rsmd.getColumnCount(); i++) { Object obj = rs.getObject(i); if (rs.wasNull()) retValue.add(null); else retValue.add(obj); } rowsArray.add(retValue); } } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } throw new DBException(e, sql); } finally { close(rs, pstmt); rs = null; pstmt = null; if (conn != null) { closeAndResetReadonlyConnection(conn); } } if (rowsArray.size() == 0) return null; return rowsArray; } /** * Create Read Replica Prepared Statement proxy * @param sql * @param trxName transaction * @return Prepared Statement (from replica if possible, otherwise normal statement) */ public static PreparedStatement prepareNormalReadReplicaStatement(String sql, String trxName) { return prepareNormalReadReplicaStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, trxName); } /** * Create Read Replica Prepared Statement proxy * @param sql * @param resultSetType - ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.TYPE_SCROLL_SENSITIVE * @param resultSetConcurrency - ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE * @param trxName transaction name * @return Prepared Statement (from replica if possible, otherwise normal statement) */ private static PreparedStatement prepareNormalReadReplicaStatement(String sql, int resultSetType, int resultSetConcurrency, String trxName) { if (sql == null || sql.length() == 0) throw new IllegalArgumentException("No SQL"); boolean useReadReplica = MSysConfig.getValue(MSysConfig.DB_READ_REPLICA_URLS) != null; if ( trxName == null && useReadReplica && resultSetType == ResultSet.TYPE_FORWARD_ONLY && resultSetConcurrency == ResultSet.CONCUR_READ_ONLY) { // this is a candidate for a read replica connection (read-only, forward-only, no-trx), try to obtain one, otherwise fallback to normal CPreparedStatement stmt = ProxyFactory.newReadReplicaPreparedStatement(resultSetType, resultSetConcurrency, sql); if (stmt != null) { return stmt; } } // return ProxyFactory.newCPreparedStatement(resultSetType, resultSetConcurrency, sql, trxName); } /** * Create IN clause for csv value * @param columnName * @param csv comma separated value * @return IN clause */ public static String inClauseForCSV(String columnName, String csv) { return inClauseForCSV(columnName, csv, false); } /** * Create IN clause for csv value * @param columnName * @param csv comma separated value * @param isNotClause true to append NOT before IN * @return IN clause */ public static String inClauseForCSV(String columnName, String csv, boolean isNotClause) { StringBuilder builder = new StringBuilder(); builder.append(columnName); if(isNotClause) builder.append(" NOT "); builder.append(" IN ("); String[] values = csv.split("[,]"); for(int i = 0; i < values.length; i++) { if (i > 0) builder.append(","); String key = values[i]; if (columnName.endsWith("_ID")) { builder.append(key); } else { if (key.startsWith("\"") && key.endsWith("\"")) { key = key.substring(1, key.length()-1); } builder.append(TO_STRING(key)); } } builder.append(")"); return builder.toString(); } /** * Create subset clause for csv value (i.e columnName is a subset of the csv value set) * @param columnName * @param csv * @return subset sql clause */ public static String subsetClauseForCSV(String columnName, String csv) { return getDatabase().subsetClauseForCSV(columnName, csv); } /** * Create intersect clause for csv value (i.e columnName is an intersect with the csv value set) * @param columnName * @param csv * @return intersect sql clause */ public static String intersectClauseForCSV(String columnName, String csv) { return intersectClauseForCSV(columnName, csv, false); } /** * Create intersect clause for csv value (i.e columnName is an intersect with the csv value set) * @param columnName * @param csv * @param isNotClause true to append NOT before the intersect clause * @return intersect sql clause */ public static String intersectClauseForCSV(String columnName, String csv, boolean isNotClause) { return getDatabase().intersectClauseForCSV(columnName, csv, isNotClause); } /** * Is sql a SELECT statement * @param sql * @return true if it is a SELECT statement */ public static boolean isSelectStatement(String sql) { String removeComments = "/\\*(?:.|[\\n\\r])*?\\*/"; String removeQuotedStrings = "'(?:.|[\\n\\r])*?'"; String removeLeadingSpaces = "^\\s+"; String cleanSql = sql.toLowerCase().replaceAll(removeComments, "").replaceAll(removeQuotedStrings, "").replaceFirst(removeLeadingSpaces, ""); if(cleanSql.matches("^select\\s.*$") && !cleanSql.contains(";")) return true; else return false; } } // DB