/** * Automatically re-connect if necessary and if configured to do so. * * @param count the retry count index * @return true if reconnected */ private boolean autoReconnect(int count) { if (!isClosed()) { return false; } if (!autoReconnect) { return false; } if (!cluster && !autoCommit) { return false; } if (count > SysProperties.MAX_RECONNECT) { return false; } lastReconnect++; while (true) { try { embedded = connectEmbeddedOrServer(false); break; } catch (DbException e) { if (e.getErrorCode() != ErrorCode.DATABASE_IS_IN_EXCLUSIVE_MODE) { throw e; } // exclusive mode: re-try endlessly try { Thread.sleep(500); } catch (Exception e2) { // ignore } } } if (embedded == this) { // connected to a server somewhere else embedded = null; } else { // opened an embedded connection now - // must connect to this database in server mode // unfortunately connectEmbeddedOrServer(true); } recreateSessionState(); if (eventListener != null) { eventListener.setProgress( DatabaseEventListener.STATE_RECONNECTED, databaseName, count, SysProperties.MAX_RECONNECT); } return true; }
/** * Open a new (remote or embedded) session. * * @param openNew whether to open a new session in any case * @return the session */ public SessionInterface connectEmbeddedOrServer(boolean openNew) { ConnectionInfo ci = connectionInfo; if (ci.isRemote()) { connectServer(ci); return this; } // create the session using reflection, // so that the JDBC layer can be compiled without it boolean autoServerMode = Boolean.parseBoolean(ci.getProperty("AUTO_SERVER", "false")); ConnectionInfo backup = null; try { if (autoServerMode) { backup = ci.clone(); connectionInfo = ci.clone(); } if (openNew) { ci.setProperty("OPEN_NEW", "true"); } if (sessionFactory == null) { sessionFactory = (SessionFactory) Class.forName("org.h2.engine.Engine").getMethod("getInstance").invoke(null); } return sessionFactory.createSession(ci); } catch (Exception re) { DbException e = DbException.convert(re); if (e.getErrorCode() == ErrorCode.DATABASE_ALREADY_OPEN_1) { if (autoServerMode) { String serverKey = ((JdbcSQLException) e.getSQLException()).getSQL(); if (serverKey != null) { backup.setServerKey(serverKey); // OPEN_NEW must be removed now, otherwise // opening a session with AUTO_SERVER fails // if another connection is already open backup.removeProperty("OPEN_NEW", null); connectServer(backup); return this; } } } throw e; } }
private void handleOnDuplicate(DbException de) { if (de.getErrorCode() != ErrorCode.DUPLICATE_KEY_1) { throw de; } if (duplicateKeyAssignmentMap == null || duplicateKeyAssignmentMap.isEmpty()) { throw de; } ArrayList<String> variableNames = new ArrayList<String>(duplicateKeyAssignmentMap.size()); for (int i = 0; i < columns.length; i++) { String key = session.getCurrentSchemaName() + "." + table.getName() + "." + columns[i].getName(); variableNames.add(key); session.setVariable(key, list.get(getCurrentRowNumber() - 1)[i].getValue(session)); } Update command = new Update(session); command.setTableFilter(new TableFilter(session, table, null, true, null)); for (Column column : duplicateKeyAssignmentMap.keySet()) { command.setAssignment(column, duplicateKeyAssignmentMap.get(column)); } Index foundIndex = searchForUpdateIndex(); if (foundIndex != null) { command.setCondition(prepareUpdateCondition(foundIndex)); } else { throw DbException.getUnsupportedException( "Unable to apply ON DUPLICATE KEY UPDATE, no index found!"); } command.prepare(); command.update(); for (String variableName : variableNames) { session.setVariable(variableName, ValueNull.INSTANCE); } }
/** * Run one recovery stage. There are three recovery stages: 0: only the undo steps are run * (restoring the state before the last checkpoint). 1: the pages that are used by the transaction * log are allocated. 2: the committed operations are re-applied. * * @param stage the recovery stage * @return whether the transaction log was empty */ boolean recover(int stage) { if (trace.isDebugEnabled()) { trace.debug("log recover stage: " + stage); } if (stage == RECOVERY_STAGE_ALLOCATE) { PageInputStream in = new PageInputStream(store, logKey, firstTrunkPage, firstDataPage); usedLogPages = in.allocateAllPages(); in.close(); return true; } PageInputStream pageIn = new PageInputStream(store, logKey, firstTrunkPage, firstDataPage); DataReader in = new DataReader(pageIn); int logId = 0; Data data = store.createData(); boolean isEmpty = true; try { int pos = 0; while (true) { int x = in.readByte(); if (x < 0) { break; } pos++; isEmpty = false; if (x == UNDO) { int pageId = in.readVarInt(); int size = in.readVarInt(); if (size == 0) { in.readFully(data.getBytes(), store.getPageSize()); } else if (size == 1) { // empty Arrays.fill(data.getBytes(), 0, store.getPageSize(), (byte) 0); } else { in.readFully(compressBuffer, size); try { compress.expand(compressBuffer, 0, size, data.getBytes(), 0, store.getPageSize()); } catch (ArrayIndexOutOfBoundsException e) { DbException.convertToIOException(e); } } if (stage == RECOVERY_STAGE_UNDO) { if (!undo.get(pageId)) { if (trace.isDebugEnabled()) { trace.debug("log undo {0}", pageId); } store.writePage(pageId, data); undo.set(pageId); undoAll.set(pageId); } else { if (trace.isDebugEnabled()) { trace.debug("log undo skip {0}", pageId); } } } } else if (x == ADD) { int sessionId = in.readVarInt(); int tableId = in.readVarInt(); Row row = readRow(in, data); if (stage == RECOVERY_STAGE_UNDO) { store.allocateIfIndexRoot(pos, tableId, row); } else if (stage == RECOVERY_STAGE_REDO) { if (isSessionCommitted(sessionId, logId, pos)) { if (trace.isDebugEnabled()) { trace.debug("log redo + table: " + tableId + " s: " + sessionId + " " + row); } store.redo(tableId, row, true); } else { if (trace.isDebugEnabled()) { trace.debug("log ignore s: " + sessionId + " + table: " + tableId + " " + row); } } } } else if (x == REMOVE) { int sessionId = in.readVarInt(); int tableId = in.readVarInt(); long key = in.readVarLong(); if (stage == RECOVERY_STAGE_REDO) { if (isSessionCommitted(sessionId, logId, pos)) { if (trace.isDebugEnabled()) { trace.debug("log redo - table: " + tableId + " s:" + sessionId + " key: " + key); } store.redoDelete(tableId, key); } else { if (trace.isDebugEnabled()) { trace.debug("log ignore s: " + sessionId + " - table: " + tableId + " " + key); } } } } else if (x == TRUNCATE) { int sessionId = in.readVarInt(); int tableId = in.readVarInt(); if (stage == RECOVERY_STAGE_REDO) { if (isSessionCommitted(sessionId, logId, pos)) { if (trace.isDebugEnabled()) { trace.debug("log redo truncate table: " + tableId); } store.redoTruncate(tableId); } else { if (trace.isDebugEnabled()) { trace.debug("log ignore s: " + sessionId + " truncate table: " + tableId); } } } } else if (x == PREPARE_COMMIT) { int sessionId = in.readVarInt(); String transaction = in.readString(); if (trace.isDebugEnabled()) { trace.debug("log prepare commit " + sessionId + " " + transaction + " pos: " + pos); } if (stage == RECOVERY_STAGE_UNDO) { int page = pageIn.getDataPage(); setPrepareCommit(sessionId, page, transaction); } } else if (x == ROLLBACK) { int sessionId = in.readVarInt(); if (trace.isDebugEnabled()) { trace.debug("log rollback " + sessionId + " pos: " + pos); } // ignore - this entry is just informational } else if (x == COMMIT) { int sessionId = in.readVarInt(); if (trace.isDebugEnabled()) { trace.debug("log commit " + sessionId + " pos: " + pos); } if (stage == RECOVERY_STAGE_UNDO) { setLastCommitForSession(sessionId, logId, pos); } } else if (x == NOOP) { // nothing to do } else if (x == CHECKPOINT) { logId++; } else if (x == FREE_LOG) { int count = in.readVarInt(); for (int i = 0; i < count; i++) { int pageId = in.readVarInt(); if (stage == RECOVERY_STAGE_REDO) { if (!usedLogPages.get(pageId)) { store.free(pageId, false); } } } } else { if (trace.isDebugEnabled()) { trace.debug("log end"); break; } } } } catch (DbException e) { if (e.getErrorCode() == ErrorCode.FILE_CORRUPTED_1) { trace.debug("log recovery stopped"); } else { throw e; } } catch (IOException e) { trace.debug("log recovery completed"); } undo = new BitField(); if (stage == RECOVERY_STAGE_REDO) { usedLogPages = null; } return isEmpty; }