/** * Preconditions: 1. xid must in ended state. * * <p>Implementation deficiency preconditions: 1. this connection must have been used to run the * transaction * * <p>Postconditions: 1. Transaction is committed */ private void commitOnePhase(Xid xid) throws XAException { try { // Check preconditions if (currentXid == null || !currentXid.equals(xid)) { // In fact, we don't know if xid is bogus, or if it just wasn't associated with this // connection. // Assume it's our fault. throw new PGXAException( GT.tr( "Not implemented: one-phase commit must be issued using the same connection that was used to start it"), XAException.XAER_RMERR); } if (state != STATE_ENDED) throw new PGXAException(GT.tr("commit called before end"), XAException.XAER_PROTO); // Preconditions are met. Commit state = STATE_IDLE; currentXid = null; conn.commit(); conn.setAutoCommit(localAutoCommitMode); } catch (SQLException ex) { throw new PGXAException(GT.tr("Error during one-phase commit"), ex, XAException.XAER_RMERR); } }
/** * Preconditions: 1. xid must be in prepared state in the server * * <p>Implementation deficiency preconditions: 1. Connection must be in idle state * * <p>Postconditions: 1. Transaction is committed */ private void commitPrepared(Xid xid) throws XAException { try { // Check preconditions. The connection mustn't be used for another // other XA or local transaction, or the COMMIT PREPARED command // would mess it up. if (state != STATE_IDLE || conn.getTransactionState() != ProtocolConnection.TRANSACTION_IDLE) throw new PGXAException( GT.tr("Not implemented: 2nd phase commit must be issued using an idle connection"), XAException.XAER_RMERR); String s = RecoveredXid.xidToString(xid); localAutoCommitMode = conn.getAutoCommit(); conn.setAutoCommit(true); Statement stmt = conn.createStatement(); try { stmt.executeUpdate("COMMIT PREPARED '" + s + "'"); } finally { stmt.close(); conn.setAutoCommit(localAutoCommitMode); } } catch (SQLException ex) { throw new PGXAException( GT.tr("Error committing prepared transaction"), ex, XAException.XAER_RMERR); } }
/** * Preconditions: 1. xid is known to the RM or it's in prepared state * * <p>Implementation deficiency preconditions: 1. xid must be associated with this connection if * it's not in prepared state. * * <p>Postconditions: 1. Transaction is rolled back and disassociated from connection */ public void rollback(Xid xid) throws XAException { if (logger.logDebug()) debug("rolling back xid = " + xid); // We don't explicitly check precondition 1. try { if (currentXid != null && xid.equals(currentXid)) { state = STATE_IDLE; currentXid = null; conn.rollback(); conn.setAutoCommit(localAutoCommitMode); } else { String s = RecoveredXid.xidToString(xid); conn.setAutoCommit(true); Statement stmt = conn.createStatement(); try { stmt.executeUpdate("ROLLBACK PREPARED '" + s + "'"); } finally { stmt.close(); } } } catch (SQLException ex) { throw new PGXAException( GT.tr("Error rolling back prepared transaction"), ex, XAException.XAER_RMERR); } }
/** * Preconditions: 1. flags must be one of TMNOFLAGS, TMRESUME or TMJOIN 2. xid != null 3. * connection must not be associated with a transaction 4. the TM hasn't seen the xid before * * <p>Implementation deficiency preconditions: 1. TMRESUME not supported. 2. if flags is TMJOIN, * we must be in ended state, and xid must be the current transaction 3. unless flags is TMJOIN, * previous transaction using the connection must be committed or prepared or rolled back * * <p>Postconditions: 1. Connection is associated with the transaction */ public void start(Xid xid, int flags) throws XAException { if (logger.logDebug()) debug("starting transaction xid = " + xid); // Check preconditions if (flags != XAResource.TMNOFLAGS && flags != XAResource.TMRESUME && flags != XAResource.TMJOIN) throw new PGXAException(GT.tr("Invalid flags"), XAException.XAER_INVAL); if (xid == null) throw new PGXAException(GT.tr("xid must not be null"), XAException.XAER_INVAL); if (state == STATE_ACTIVE) throw new PGXAException( GT.tr("Connection is busy with another transaction"), XAException.XAER_PROTO); // We can't check precondition 4 easily, so we don't. Duplicate xid will be catched in prepare // phase. // Check implementation deficiency preconditions if (flags == TMRESUME) throw new PGXAException(GT.tr("suspend/resume not implemented"), XAException.XAER_RMERR); // It's ok to join an ended transaction. WebLogic does that. if (flags == TMJOIN) { if (state != STATE_ENDED) throw new PGXAException( GT.tr("Transaction interleaving not implemented"), XAException.XAER_RMERR); if (!xid.equals(currentXid)) throw new PGXAException( GT.tr("Transaction interleaving not implemented"), XAException.XAER_RMERR); } else if (state == STATE_ENDED) throw new PGXAException( GT.tr("Transaction interleaving not implemented"), XAException.XAER_RMERR); try { localAutoCommitMode = conn.getAutoCommit(); conn.setAutoCommit(false); } catch (SQLException ex) { throw new PGXAException(GT.tr("Error disabling autocommit"), ex, XAException.XAER_RMERR); } // Preconditions are met, Associate connection with the transaction state = STATE_ACTIVE; currentXid = xid; }
/** * Preconditions: 1. xid != null 2. xid is in ended state * * <p>Implementation deficiency preconditions: 1. xid was associated with this connection * * <p>Postconditions: 1. Transaction is prepared */ public int prepare(Xid xid) throws XAException { if (logger.logDebug()) debug("preparing transaction xid = " + xid); // Check preconditions if (!currentXid.equals(xid)) { throw new PGXAException( GT.tr( "Not implemented: Prepare must be issued using the same connection that started the transaction"), XAException.XAER_RMERR); } if (state != STATE_ENDED) throw new PGXAException(GT.tr("Prepare called before end"), XAException.XAER_INVAL); state = STATE_IDLE; currentXid = null; if (!conn.haveMinimumServerVersion("8.1")) throw new PGXAException( GT.tr("Server versions prior to 8.1 do not support two-phase commit."), XAException.XAER_RMERR); try { String s = RecoveredXid.xidToString(xid); Statement stmt = conn.createStatement(); try { stmt.executeUpdate("PREPARE TRANSACTION '" + s + "'"); } finally { stmt.close(); } conn.setAutoCommit(localAutoCommitMode); return XA_OK; } catch (SQLException ex) { throw new PGXAException(GT.tr("Error preparing transaction"), ex, XAException.XAER_RMERR); } }
/** * Returns the fully-qualified name of the Java class whose instances are manufactured if the * method <code>ResultSet.getObject</code> is called to retrieve a value from the column. <code> * ResultSet.getObject</code> may return a subclass of the class returned by this method. * * @param column the first column is 1, the second is 2, ... * @return the fully-qualified name of the class in the Java programming language that would be * used by the method <code>ResultSet.getObject</code> to retrieve the value in the specified * column. This is the class name used for custom mapping. * @exception SQLException if a database access error occurs */ public String getColumnClassName(int column) throws SQLException { Field field = getField(column); String result = connection.getTypeInfo().getJavaClass(field.getOID()); if (result != null) return result; int sqlType = getSQLType(column); switch (sqlType) { case Types.ARRAY: return ("java.sql.Array"); default: String type = getPGType(column); if ("unknown".equals(type)) { return ("java.lang.String"); } return ("java.lang.Object"); } }
/** * Preconditions: 1. flag must be one of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS or TMSTARTTRSCAN | * TMENDRSCAN 2. if flag isn't TMSTARTRSCAN or TMSTARTRSCAN | TMENDRSCAN, a recovery scan must be * in progress * * <p>Postconditions: 1. list of prepared xids is returned */ public Xid[] recover(int flag) throws XAException { // Check preconditions if (flag != TMSTARTRSCAN && flag != TMENDRSCAN && flag != TMNOFLAGS && flag != (TMSTARTRSCAN | TMENDRSCAN)) throw new PGXAException(GT.tr("Invalid flag"), XAException.XAER_INVAL); // We don't check for precondition 2, because we would have to add some additional state in // this object to keep track of recovery scans. // All clear. We return all the xids in the first TMSTARTRSCAN call, and always return // an empty array otherwise. if ((flag & TMSTARTRSCAN) == 0) return new Xid[0]; else { try { Statement stmt = conn.createStatement(); try { // If this connection is simultaneously used for a transaction, // this query gets executed inside that transaction. It's OK, // except if the transaction is in abort-only state and the // backed refuses to process new queries. Hopefully not a problem // in practise. ResultSet rs = stmt.executeQuery("SELECT gid FROM pg_prepared_xacts"); LinkedList l = new LinkedList(); while (rs.next()) { Xid recoveredXid = RecoveredXid.stringToXid(rs.getString(1)); if (recoveredXid != null) l.add(recoveredXid); } rs.close(); return (Xid[]) l.toArray(new Xid[l.size()]); } finally { stmt.close(); } } catch (SQLException ex) { throw new PGXAException(GT.tr("Error during recover"), ex, XAException.XAER_RMERR); } } }
/** {@inheritDoc} PostgreSQL doesn't have unsigned numbers */ public boolean isSigned(int param) throws SQLException { checkParamIndex(param); return _connection.getTypeInfo().isSigned(_oids[param - 1]); }
public String getParameterTypeName(int param) throws SQLException { checkParamIndex(param); return _connection.getTypeInfo().getPGType(_oids[param - 1]); }
public PGXAConnection(BaseConnection conn) throws SQLException { super(conn, true, true); this.conn = conn; this.state = STATE_IDLE; this.logger = conn.getLogger(); }
/* * Does a column's case matter? ASSUMPTION: Any field that is * not obviously case insensitive is assumed to be case sensitive * * @param column the first column is 1, the second is 2... * @return true if so * @exception SQLException if a database access error occurs */ public boolean isCaseSensitive(int column) throws SQLException { Field field = getField(column); return connection.getTypeInfo().isCaseSensitive(field.getOID()); }
protected int getSQLType(int columnIndex) throws SQLException { return connection.getTypeInfo().getSQLType(getField(columnIndex).getOID()); }
/* * What is a column's number of digits to the right of the * decimal point? * * @param column the first column is 1, the second is 2... * @return the scale * @exception SQLException if a database access error occurs */ public int getScale(int column) throws SQLException { Field field = getField(column); return connection.getTypeInfo().getScale(field.getOID(), field.getMod()); }
private void fetchFieldMetaData() throws SQLException { if (fieldInfoFetched) return; fieldInfoFetched = true; StringBuffer sql = new StringBuffer(); sql.append("SELECT c.oid, a.attnum, a.attname, c.relname, n.nspname, "); sql.append("a.attnotnull OR (t.typtype = 'd' AND t.typnotnull), "); sql.append("pg_catalog.pg_get_expr(d.adbin, d.adrelid) LIKE '%nextval(%' "); sql.append("FROM pg_catalog.pg_class c "); sql.append("JOIN pg_catalog.pg_namespace n ON (c.relnamespace = n.oid) "); sql.append("JOIN pg_catalog.pg_attribute a ON (c.oid = a.attrelid) "); sql.append("JOIN pg_catalog.pg_type t ON (a.atttypid = t.oid) "); sql.append( "LEFT JOIN pg_catalog.pg_attrdef d ON (d.adrelid = a.attrelid AND d.adnum = a.attnum) "); sql.append("JOIN ("); // 7.4 servers don't support row IN operations (a,b) IN ((c,d),(e,f)) // so we've got to fake that with a JOIN here. // boolean hasSourceInfo = false; for (int i = 0; i < fields.length; i++) { if (fields[i].getTableOid() == 0) continue; if (hasSourceInfo) sql.append(" UNION ALL "); sql.append("SELECT "); sql.append(fields[i].getTableOid()); if (!hasSourceInfo) sql.append(" AS oid "); sql.append(", "); sql.append(fields[i].getPositionInTable()); if (!hasSourceInfo) sql.append(" AS attnum"); if (!hasSourceInfo) hasSourceInfo = true; } sql.append(") vals ON (c.oid = vals.oid AND a.attnum = vals.attnum) "); if (!hasSourceInfo) return; Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(sql.toString()); while (rs.next()) { int table = (int) rs.getLong(1); int column = (int) rs.getLong(2); String columnName = rs.getString(3); String tableName = rs.getString(4); String schemaName = rs.getString(5); int nullable = rs.getBoolean(6) ? ResultSetMetaData.columnNoNulls : ResultSetMetaData.columnNullable; boolean autoIncrement = rs.getBoolean(7); for (int i = 0; i < fields.length; i++) { if (fields[i].getTableOid() == table && fields[i].getPositionInTable() == column) { fields[i].setColumnName(columnName); fields[i].setTableName(tableName); fields[i].setSchemaName(schemaName); fields[i].setNullable(nullable); fields[i].setAutoIncrement(autoIncrement); } } } stmt.close(); }