Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   *
   * Firebird Open Source J2ee connector - jdbc driver
   *
   * Distributable under LGPL license.
   * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
   *
   * 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
  * LGPL License for more details.
  *
  * This file was created by members of the firebird development team.
  * All individual contributions remain the Copyright (C) of those
  * individuals.  Contributors to this file are either listed here or
  * can be obtained from a CVS history command.
  *
  * All rights reserved.
  */
 package org.firebirdsql.jdbc;
 
 
Class responsible for modifying updatable result sets. A result set is updatable if and only if:
  • It is a subset of a single table and includes all columns from the table's primary key (in other words, includes all best row identifiers) or RDB$DB_KEY column (in this case tables without primary key can be updated too).
  • If base table columns not included in the result set allow NULL values, result set allows inserting rows into it.
  • The result set's SELECT statement does not contain subqueries, a DISTINCT predicate, a HAVING clause, aggregate functions, joined tables, user-defined functions, or stored procedures.
If the result set definition does not meet these conditions, it is considered read-only.

Author(s):
Roman Rokytskyy
 
 public class FBRowUpdater implements FirebirdRowUpdater  {
     
     private static final int PARAMETER_UNUSED = 0;
     private static final int PARAMETER_USED = 1;
     private static final int PARAMETER_DBKEY = 2;
 
     private GDSHelper gdsHelper;
     private Synchronizable syncProvider;
     private XSQLVAR[] xsqlvars;
     
     private FBField[] fields;
     
     private boolean inInsertRow;
     
     private byte[][] newRow;
     private byte[][] oldRow;
     private byte[][] insertRow;
     private boolean[] updatedFlags;
 
     private String tableName;
     
 
     private boolean closed;
     private boolean processing;
     
     public FBRowUpdater(GDSHelper connectionXSQLVAR[] xsqlvars
             Synchronizable syncProviderboolean cached
             FBObjectListener.ResultSetListener rsListenerthrows SQLException {
         
         this. = rsListener;
         
         this. = connection;
         this. = syncProvider;
         
         this. = new XSQLVAR[xsqlvars.length];
         this. = new FBField[xsqlvars.length];
         
        for (int i = 0; i < xsqlvars.lengthi++) {
            XSQLVAR xsqlvar = xsqlvars[i].deepCopy();
            this.[i] = xsqlvar;
        }
        
         = new byte[xsqlvars.length][];
         = new boolean[xsqlvars.length];
        
        for (int i = 0; i < this..lengthi++) {
            
            final int fieldPos = i;
            
            // implementation of the FieldDataProvider interface
            FieldDataProvider dataProvider = new FieldDataProvider() {
                public byte[] getFieldData() {
                    if ([fieldPos]) {
                        
                        if ()
                            return [fieldPos];
                        else
                            return [fieldPos];
                    } else
                        return [fieldPos];
                }
                public void setFieldData(byte[] data) {
                    if ()
                        [fieldPos] = data;
                    else
                        [fieldPos] = data;
                    
                    [fieldPos] = true;
                }
            };
            [i] = FBField.createField(this.[i], dataProviderconnectioncached);
        }
        
        // find the table name (there can be only one table per result set)
        for (int i = 0; i < xsqlvars.lengthi++) {
            if ( == null)
                 = xsqlvars[i].;
            else
            if (!.equals(xsqlvars[i].))
                throw new FBResultSetNotUpdatableException(
                    "Underlying result set references at least two relations: " + 
                     + " and " + xsqlvars[i]. + ".");
        }
    }
    
    private void notifyExecutionStarted() throws SQLException {
        if ()
            throw new FBSQLException("Corresponding result set is closed.");
        
        if ()
            return;
        
        .executionStarted(this);
        this. = true;
    }
    
    private void notifyExecutionCompleted(boolean successthrows SQLException {
        if (!)
            return;
        
        .executionCompleted(thissuccess);
        this. = false;
    }
    private void deallocateStatement(AbstractIscStmtHandle handleSQLExceptionChainBuilder chain) {
    	try {
    		if (handle != null)
    			.closeStatement(handletrue);
    	} catch(GDSException ex) {
    	    chain.append(new FBSQLException(ex));
    	}
    }
    
    public void close() throws SQLException {
    	
    	
    	// TODO: Close not completed by throw at this point?
    	if (chain.hasException())
    		throw chain.getException();
    	
        this. = true;
        if ()
            notifyExecutionCompleted(true);
    }
    
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#setRow(byte[][])
     */
    public void setRow(byte[][] row) {
        this. = row;
        this. = new boolean[.];
        this. = false;
    }
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#cancelRowUpdates()
     */
    public void cancelRowUpdates() {
        this. = new byte[.][];
        this. = new boolean[.];
        this. = false;
    }
    
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#getField(int)
     */
    public FBField getField(int fieldPosition) {
        return [fieldPosition];
    }
    
    
This method gets the parameter mask for the UPDATE or DELETE statement. Parameter mask is an array of booleans, where array item is set to true, if the appropriate field should be included in WHERE clause of the UPDATE or DELETE statement.

This method obtains the parameter mask from the best row identifiers, in other words set of columns that form "best row identifiers" must be a subset of the selected columns (no distinction is made whether columns are real or are pseudo-columns). If no

Returns:
array of booleans that represent parameter mask.
    private int[] getParameterMask() throws SQLException {
        // loop through the "best row identifiers" and set appropriate falgs.
        FBDatabaseMetaData metaData = new FBDatabaseMetaData();
        
        ResultSet bestRowIdentifier = metaData.getBestRowIdentifier(
            """".true);
        try {
            int[] result = new int[.];
            boolean hasParams = false;
            while(bestRowIdentifier.next()) {
                String columnName = bestRowIdentifier.getString(2);
                
                if (columnName == null)
                    continue;
                
                boolean found = false;
                for (int i = 0; i < .i++) {
                    // special handling for the RDB$DB_KEY columns that must be
                    // selected as RDB$DB_KEY, but in XSQLVAR are represented
                    // as DB_KEY
                    if ("RDB$DB_KEY".equals(columnName) && "DB_KEY".equals([i].)) {
                        result[i] = ;
                        found = true;
                    } else
                    if (columnName.equals([i].)) {
                        result[i] = ;
                        found = true;
                    } 
                    //else
                    //    result[i] = PARAMETER_UNUSED;
                }
                
                // if we did not found a column from the best row identifier
                // in our result set, throw an exception, since we cannot
                // reliably identify the row.
                if (!found)
                    throw new FBResultSetNotUpdatableException(
                        "Underlying result set does not contain all columns " +
                        "that form 'best row identifier'.");
                else
                    hasParams = true;
            }
            
            if (!hasParams)
                throw new FBResultSetNotUpdatableException(
                    "No columns that can be used in WHERE clause could be " +
                    "found.");
            
            return result;
        } finally {
            bestRowIdentifier.close();
        }
    }
    
    private void appendWhereClause(StringBuffer sbint[] parameterMask) {
        sb.append("WHERE");
        sb.append("\n");
        
        // handle the RDB$DB_KEY case first
        boolean hasDbKey = false;
        for (int i = 0; i < parameterMask.lengthi++) {
            if (parameterMask[i] == ) {
                hasDbKey = true;
                break;
            }
        }
        
        if (hasDbKey) {
            if (.getDatabaseProductMajorVersion() == 3) {
                // TODO Remove Workaround for CORE-4255 when fixed (and released as alpha 2)
                sb.append("RDB$DB_KEY = CAST(? AS CHAR(8) CHARACTER SET OCTETS)");
            } else {
                sb.append("RDB$DB_KEY = ?");
            }
            return;
        }
        
        // if we are here, then no RDB$DB_KEY update was used
        // therefore loop through the parameters and build the
        // WHERE clause
        boolean first = true;
        for (int i = 0; i < .i++) {
            if (parameterMask[i] == )
                continue;
            
            if (!first)
                sb.append("AND");
            
            sb.append("\n\t");
            sb.append("\"").append([i].).append("\" = ").append("?");
            
            first = false;
        }
    }
    
    private String buildUpdateStatement(int[] parameterMask) {
        StringBuffer sb = new StringBuffer();
        
        sb.append("UPDATE ").append().append("\n");
        sb.append("SET").append("\n");
        
        boolean first = true;
        for (int i = 0; i < .i++) {
            if (![i])
                continue;
            
            if (!first)
                sb.append(",");
            
            sb.append("\n\t");
            sb.append("\"").append([i].).append("\" = ").append("?");
            
            first = false;
        }
        
        sb.append("\n");
        appendWhereClause(sbparameterMask);
        
        return sb.toString();
    }
    
    private String buildDeleteStatement(int[] parameterMask) {
        StringBuffer sb = new StringBuffer();
        
        sb.append("DELETE FROM ").append().append("\n");
        appendWhereClause(sbparameterMask);
        
        return sb.toString();
    }
    
    private String buildInsertStatement() {
        StringBuffer sb = new StringBuffer();
        
        StringBuffer columns = new StringBuffer();
        StringBuffer params = new StringBuffer();
        
        sb.append("INSERT INTO ").append();
        
        boolean first = true;
        for (int i = 0; i < .i++) {
            
            if (![i])
                continue;
            
            if (!first) {
                columns.append(", ");
                params.append(", ");
            }
            
            columns.append([i].);
            params.append("?");
            
            first = false;
        }
        
        sb.append("(\n\t").append(columns).append("\n)");
        sb.append("VALUES");
        sb.append("(\n\t").append(params).append("\n)");
        
        return sb.toString();
    }
    
    private String buildSelectStatement(int[] parameterMask) {
        StringBuffer sb = new StringBuffer();
        StringBuffer columns = new StringBuffer();
        
        sb.append("SELECT");
        
        boolean first = true;
        for (int i = 0; i < .i++) {
            
            if (!first
                columns.append(", ");
            
            // do special handling of RDB$DB_KEY, since Firebird returns
            // DB_KEY column name instead of the correct one
            if ("DB_KEY".equals([i].)
                    && (([i]. & ~1) == .)
                    && [i]. == 8)
                columns.append("RDB$DB_KEY");
            else
                columns.append("\"").append([i].).append("\"");
            
            first = false;
        }
        
        sb.append("\n\t").append(columns).append("\n");
        sb.append("FROM");
        sb.append("\n\t").append().append("\n");
        appendWhereClause(sbparameterMask);
        return sb.toString();
    }
    
    private static final int UPDATE_STATEMENT_TYPE = 1;
    private static final int DELETE_STATEMENT_TYPE = 2;
    private static final int INSERT_STATEMENT_TYPE = 3;
    private static final int SELECT_STATEMENT_TYPE = 4;
    
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#updateRow()
     */
    public void updateRow() throws SQLException {
        
        boolean success = false;
        
        Object mutex = .getSynchronizationObject();
        synchronized(mutex) {
            try {
                
                notifyExecutionStarted();
                
                if ( == null)
                     = .allocateStatement();
                
                executeStatement();
                
                success = true;
                
            } catch(GDSException ex) {
                throw new FBSQLException(ex);
            } finally {
                notifyExecutionCompleted(success);
            }
        }
    }
    
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#deleteRow()
     */
    public void deleteRow() throws SQLException {
        
        boolean success = false;
        
        Object mutex = .getSynchronizationObject();
        synchronized(mutex) {
            try {
                
                notifyExecutionStarted();
                
                if ( == null)
                     = .allocateStatement();
                
                executeStatement();
                
                success = true;
                
            } catch(GDSException ex) {
                throw new FBSQLException(ex);
            } finally {
                notifyExecutionCompleted(success);
            }
        }
    }
    
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#insertRow()
     */
    public void insertRow() throws SQLException {
        
        boolean success = false;
        
        Object mutex = .getSynchronizationObject();
        synchronized(mutex) {
            try {
                
                notifyExecutionStarted();
                
                if ( == null)
                     = .allocateStatement();
                
                executeStatement();
                
                success = true;
                
            } catch(GDSException ex) {
                throw new FBSQLException(ex);
            } finally {
                notifyExecutionCompleted(success);
            }
        }
    }
    
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#refreshRow()
     */
    public void refreshRow() throws SQLException {
        
        boolean success = false;
        
        Object mutex = .getSynchronizationObject();
        synchronized(mutex) {
            try {
                
                notifyExecutionStarted();
                
                if ( == null)
                     = .allocateStatement();
                
                try {
                    executeStatement();
                    
                    // should fetch one row anyway
                    .fetch(, 10);
                    
                    Object[] rows = .getRows();
                    if (.size() == 0)
                        throw new FBSQLException("No rows could be fetched.");
                    
                    if (.size() > 1)
                    throw new FBSQLException("More then one row fetched.");
                    
                    setRow((byte[][])rows[0]);
                } finally {
                    .closeStatement(false);
                     = null;
                }
                
                success = true;
                
            } catch(GDSException ex) {
                throw new FBSQLException(ex);
            } finally {
                notifyExecutionCompleted(success);
            }
        }
    }
    
    private void executeStatement(int statementTypeAbstractIscStmtHandle stmtthrows SQLException {
        try {
            if (!stmt.isValid())
                throw new FBSQLException("Corresponding connection is not valid.",
                    .);
            
            if ( && statementType != )
                throw new FBSQLException("Only insertRow() is allowed when " +
                        "result set is positioned on insert row.");
            
            if (statementType !=  &&  == null)
                throw new FBSQLException("Result set is not positioned on a row.");
            // we have to flush before constructing the parameters
            // since flushable field can update the value, which 
            // in turn can change the parameter distribution
            for (int i = 0; i < .i++) {
                if ([iinstanceof FBFlushableField)
                    ((FBFlushableField)[i]).flushCachedData();
            }
            
            int[] parameterMask = getParameterMask();
            
            String sql;
            switch(statementType) {
                case  :
                    sql = buildUpdateStatement(parameterMask);
                    break;
                    
                case  :
                    sql = buildDeleteStatement(parameterMask);
                    break;
                    
                case  :
                    sql = buildInsertStatement();
                    break;
                    
                case  :
                    sql = buildSelectStatement(parameterMask);
                    break;
                    
                default :
                    throw new IllegalArgumentException(
                        "Incorrect statement type specified.");
            }
            
            .prepareStatement(stmtsqltrue);
            XSQLVAR[] params = stmt.getInSqlda().;
            
            int paramIterator = 0;
            
            if (statementType == ) {
                for (int i = 0; i < .i++) {
                    if (![i])
                        continue;
                    
                    params[paramIterator].copyFrom([i]);
                    params[paramIterator]. = [i];
                    paramIterator++;
                }
            }
            
            for (int i = 0; i < .i++) {
                if (parameterMask[i] ==  && statementType != )
                    continue;
                else
                if (![i] && statementType == )
                    continue;
                
                params[paramIterator].copyFrom([i]);
                
                if (statementType == )
                    params[paramIterator]. = [i];
                else
                    params[paramIterator]. = [i];
                
                paramIterator++;
            }
            
            .executeStatement(stmtfalse);
            
            // TODO think about adding COMMIT RETAIN in the auto-commit mode
            
        } catch(GDSException ex) {
            throw new FBSQLException(ex);
        }
    }
    
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#rowInserted()
     */
    public boolean rowInserted() throws SQLException {
        return false;
    }
    
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#rowDeleted()
     */
    public boolean rowDeleted() throws SQLException {
        return false;
    }
    
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#rowUpdated()
     */
    public boolean rowUpdated() throws SQLException {
        return false;
    }
    
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#getNewRow()
     */
    public byte[][] getNewRow() {
        byte[][] result = new byte[.][];
        for (int i = 0; i < result.lengthi++) {
            if ([i]) {
                if ([i] == null)
                    result[i] = null;
                else {
                    result[i] = new byte[[i].length];
                    System.arraycopy([i], 0, result[i], 0, [i].length);
                }
            } else {
                if ([i] == null) { 
                    result[i] = null;
                } else {
                    result[i] = new byte[[i].length];
                    System.arraycopy([i], 0, result[i], 0, [i].length);
                }
            }
        }
        
        return result;
    }
    
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#getInsertRow()
     */
    public byte[][] getInsertRow() {
        return ;
    }
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#getOldRow()
     */
    public byte[][] getOldRow() {
        return ;
    }
    
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#moveToInsertRow()
     */
    public void moveToInsertRow() throws SQLException {
         = true;
         = new byte[.][];
        this. = new boolean[.];
    }
    
    /* (non-Javadoc)
     * @see org.firebirdsql.jdbc.FirebirdRowUpdater#moveToCurrentRow()
     */
    public void moveToCurrentRow() throws SQLException {
         = false;
         = new byte[.][];
        this. = new boolean[.];
    }
New to GrepCode? Check out our FAQ X