Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * $Id: AbstractGeneratedKeysQuery.java 56937 2012-04-04 18:31:34Z mrotteveel $
   * 
   * 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;
 
 import java.util.List;
 
Class to add the RETURNING clause to queries for returning generated keys.

Author(s):
Mark Rotteveel
Since:
2.2
 
 public abstract class AbstractGeneratedKeysQuery {
 
     private static final int QUERY_TYPE_KEEP_UNMODIFIED = 1;
     private static final int QUERY_TYPE_ADD_ALL_COLUMNS = 2;
     private static final int QUERY_TYPE_ADD_INDEXED = 3;
     private static final int QUERY_TYPE_ADD_COLUMNS = 4;
     private static final int QUERY_TYPE_ALREADY_HAS_RETURNING = 5;
     
     private static final StatementParser parser;
     static {
     	// Attempt to load statement parser
         StatementParser temp = null;
         try {
             temp = (StatementParser)Class.forName("org.firebirdsql.jdbc.parser.StatementParserImpl").newInstance();
         } catch (Throwable ex) {
             // Unable to load class of parser implementation, antlr-runtime not in path
             Logger log = LoggerFactory.getLogger(AbstractGeneratedKeysQuery.classtrue);
             if (log != null) {
                 log.error("Unable to load generated key parser"ex);
             }
         } finally {
              = temp;
         }
     }
 
     private final String originalSQL;
     private String modifiedSQL;
     private int queryType = ;
     private int[] columnIndexes;
     private String[] columnNames;
     private boolean processed = false;
     private boolean generatesKeys = false;
 
     private AbstractGeneratedKeysQuery(String sql) {
          = sql;
     }

    
Process SQL statement text according to autoGeneratedKeys value.

For Statement.NO_GENERATED_KEYS the statement will not be processed, for Statement.RETURN_GENERATED_KEYS it will be processed.

The query will only be modified if 1) it is capable of returning keys (ie INSERT, DELETE and UPDATE) and 2) does not already contain a RETURNING clause.

Parameters:
sql SQL statement
autoGeneratedKeys Valid values java.sql.Statement.NO_GENERATED_KEYS and java.sql.Statement.RETURN_GENERATED_KEYS
Throws:
java.sql.SQLException If the supplied autoGeneratedKeys value does not match valid values or if the parser cannot be loaded when autoGeneratedKeys = RETURN_GENERATED_KEYS.
    public AbstractGeneratedKeysQuery(String sqlint autoGeneratedKeysthrows SQLException {
        this(sql);
        
        switch (autoGeneratedKeys) {
        case .:
            if ( == null) {
                throw new FBDriverNotCapableException("Generated keys functionality not available, most likely cause: ANTLR-Runtime not available on classpath");
            }
             = ;
            break;
        case .:
             = ;
            break;
        default:
            throw new FBSQLException("Supplied value for autoGeneratedKeys is invalid".);
        }
    }

    
Process SQL statement for adding generated key columns by their ordinal position.

The query will only be modified if 1) it is capable of returning keys (ie INSERT, DELETE and UPDATE) and 2) does not already contain a RETURNING clause.

The columns are added in ascending order of their index value, not by the order of indexes in the columnIndexes array. The values of columnIndexes are taken as the ORDINAL_POSITION returned by java.sql.DatabaseMetaData.getColumns(java.lang.String,java.lang.String,java.lang.String,java.lang.String) . When a column index does not exist for the table of the query, then it will be discarded from the list silently.

Parameters:
sql SQL statement
columnIndexes Array of ORDINAL_POSITION values of the columns to return as generated key
Throws:
java.sql.SQLException If the parser cannot be loaded
    public AbstractGeneratedKeysQuery(String sqlint[] columnIndexesthrows SQLException {
        this(sql);
        if ( == null) {
            throw new FBDriverNotCapableException("Generated keys functionality not available, most likely cause: ANTLR-Runtime not available on classpath");
        } else if (columnIndexes != null && columnIndexes.length != 0) {
            this. = columnIndexes.clone();
             = ;
        } else {
             = ;
        }
    }

    
Process SQL statement for adding generated key columns by name.

The query will only be modified if 1) it is capable of returning keys (ie INSERT, DELETE and UPDATE) and 2) does not already contain a RETURNING clause.

The columnNames passed are taken as is and included in a new returning clause. There is no check for actual existence of these columns, nor are they quoted.

Parameters:
sql SQL statement
columnNames Array of column names to return as generated key
Throws:
java.sql.SQLException If the parser cannot be loaded
    public AbstractGeneratedKeysQuery(String sqlString[] columnNamesthrows SQLException {
        this(sql);
        if ( == null) {
            throw new FBDriverNotCapableException("Generated keys functionality not available, most likely cause: ANTLR-Runtime not available on classpath");
        } else if (columnNames != null && columnNames.length != 0) {
            this. = columnNames.clone();
             = ;
        } else {
             = ;
        }
    }

    
Indicates if the query will generate keys.

Returns:
true if the query will generate keys, false otherwise
Throws:
java.sql.SQLException For errors accessing the metadata
    public boolean generatesKeys() throws SQLException {
        process();
        return ;
    }

    
Returns the actual query.

Use generatesKeys() to see if this query will in fact generate keys.

Returns:
The SQL query
Throws:
java.sql.SQLException For errors accessing the metadata
    public String getQueryString() throws SQLException {
        process();
        return ;
    }

    
Parses the query and updates the query with generated keys if modifications are needed or possible.

Throws:
java.sql.SQLException For errors accessing the metadata
    private void process() throws SQLException {
        if () {
            return;
        }
        try {
            processStatementModel();
            updateQuery();
        } finally {
             = true;
        }
    }

    
Parses the original SQL query and checks if it already has a RETURNING clause

Throws:
java.sql.SQLException If query parsing is not available
    private void processStatementModel() throws SQLException {
        if ( == null) {
            if ( == ) {
                // JDBC specifies that NO_GENERATED_KEYS (signified here by QUERY_TYPE_KEEP_UNMODIFIED) 
            	// should not result in failure if processing generated keys is not possible
                return;
            } else {
                // This condition should already have been caught in constructors, but do it anyway:
                throw new FBDriverNotCapableException("Generated keys functionality not available, most likely cause: ANTLR-Runtime not available on classpath");
            }
        }
        try {
             = parseInsertStatement();
            if (.getReturningColumns().size() > 0) {
                 = ;
            }
        } catch (ParseException e) {
            // Unrecognized statement (so no INSERT, DELETE, UPDATE or UPDATE OR
            // INSERT statement), keep as is
             = ;
        }
    }

    
Adds the generated key columns to the query.

Throws:
java.sql.SQLException For errors accessing the metadata
    private void updateQuery() throws SQLException {
        switch () {
        case :
            addAllColumns();
            break;
        case :
            addIndexedColumns();
            break;
        case :
            addReturningClause();
            break;
             = true;
             = ;
            break;
        case :
        	// Do nothing
        	break;
        default:
            throw new IllegalStateException("Unsupported value for queryType: " + );
        }
        // Not part of switch: elements of switch will modify queryType (eg when
        // nothing is added)
        if ( == ) {
             = ;
        }
    }

    
Adds all available table columns to the query as generated keys.

Throws:
java.sql.SQLException For errors accessing the metadata
    private void addAllColumns() throws SQLException {
        DatabaseMetaData metaData = getDatabaseMetaData();
        ResultSet rs = metaData.getColumns(nullnull.getTableName(), null);
        List<Stringcolumns = new ArrayList<String>();
        try {
            while (rs.next()) {
            	// Need to quote columns for mixed case columns
                columns.add('"' + rs.getString(4) + '"');
            }
        } finally {
            rs.close();
        }
         = (String[]) columns.toArray(new String[0]);
        addReturningClause();
    }

    
Adds all columns referenced by columnIndexes to the query as generated keys.

Throws:
java.sql.SQLException For errors accessing the metadata
    private void addIndexedColumns() throws SQLException {
        DatabaseMetaData metaData = getDatabaseMetaData();
        ResultSet rs = metaData.getColumns(nullnull.getTableName(), null);
        Arrays.sort();
        List<Stringcolumns = new ArrayList<String>();
        try {
            while (rs.next()) {
                if (Arrays.binarySearch(rs.getInt(17)) >= 0) {
                	// Need to quote columns for mixed case columns
                    columns.add('"' + rs.getString(4) + '"');
                }
            }
        } finally {
            rs.close();
        }
         = (String[]) columns.toArray(new String[0]);
        addReturningClause();
    }

    
Adds the columns in columnNames to the query as generated keys.
    private void addReturningClause() {
        if ( == null || . == 0) {
             = ;
            return;
        }
         = true;
        StringBuffer query = new StringBuffer();
        if (query.charAt(query.length() - 1) == ';') {
            query.deleteCharAt(query.length() - 1);
        }
        query.append('\n');
        query.append("RETURNING ");
        for (int i = 0; i < .i++) {
            query.append([i]);
            if (i < . - 1) {
                query.append(", ");
            }
        }
         = query.toString();
    }

    
Returns the DatabaseMetaData object to be used when processing this query. In general this should be a DatabaseMetaData object created from the connection which will execute the query.

Returns:
DatabaseMetaData object
Throws:
java.sql.SQLException if a database access error occurs
    abstract DatabaseMetaData getDatabaseMetaData() throws SQLException;

    
Parse the INSERT statement and extract the corresponding model.

Parameters:
sql SQL statement to parse.
Returns:
instance of org.firebirdsql.jdbc.parser.JaybirdStatementModel
Throws:
org.firebirdsql.jdbc.parser.StatementParser.ParseException if statement cannot be parsed.
        return .parseInsertStatement(sql);
    }
New to GrepCode? Check out our FAQ X