| /* |
| * Copyright (c) 2007-2013, 2015 Eike Stepper (Berlin, Germany) and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Eike Stepper - initial API and implementation |
| * Stefan Winkler - Bug 259402 |
| * Stefan Winkler - Bug 271444: [DB] Multiple refactorings bug 271444 |
| * Stefan Winkler - Bug 249610: [DB] Support external references (Implementation) |
| * Stefan Winkler - Bug 289056: [DB] Exception "ERROR: relation "cdo_external_refs" does not exist" while executing test-suite for PostgreSQL |
| */ |
| package org.eclipse.emf.cdo.server.internal.db; |
| |
| import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation; |
| import org.eclipse.emf.cdo.common.branch.CDOBranch; |
| import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; |
| import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; |
| import org.eclipse.emf.cdo.common.id.CDOID; |
| import org.eclipse.emf.cdo.common.revision.CDOAllRevisionsProvider; |
| import org.eclipse.emf.cdo.common.revision.CDORevision; |
| import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; |
| import org.eclipse.emf.cdo.server.ISession; |
| import org.eclipse.emf.cdo.server.ITransaction; |
| import org.eclipse.emf.cdo.server.IView; |
| import org.eclipse.emf.cdo.server.StoreThreadLocal; |
| import org.eclipse.emf.cdo.server.db.IDBStore; |
| import org.eclipse.emf.cdo.server.db.IIDHandler; |
| import org.eclipse.emf.cdo.server.db.IMetaDataManager; |
| import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; |
| import org.eclipse.emf.cdo.server.internal.db.bundle.OM; |
| import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.IMappingConstants; |
| import org.eclipse.emf.cdo.server.internal.db.messages.Messages; |
| import org.eclipse.emf.cdo.spi.server.InternalRepository; |
| import org.eclipse.emf.cdo.spi.server.InternalSession; |
| import org.eclipse.emf.cdo.spi.server.LongIDStoreAccessor; |
| import org.eclipse.emf.cdo.spi.server.Store; |
| import org.eclipse.emf.cdo.spi.server.StoreAccessorPool; |
| |
| import org.eclipse.net4j.db.DBException; |
| import org.eclipse.net4j.db.DBUtil; |
| import org.eclipse.net4j.db.IDBAdapter; |
| import org.eclipse.net4j.db.IDBConnection; |
| import org.eclipse.net4j.db.IDBConnectionProvider; |
| import org.eclipse.net4j.db.IDBDatabase; |
| import org.eclipse.net4j.db.IDBPreparedStatement; |
| import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; |
| import org.eclipse.net4j.db.IDBSchemaTransaction; |
| import org.eclipse.net4j.db.ddl.IDBField; |
| import org.eclipse.net4j.db.ddl.IDBSchema; |
| import org.eclipse.net4j.db.ddl.IDBTable; |
| import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; |
| import org.eclipse.net4j.util.lifecycle.LifecycleUtil; |
| import org.eclipse.net4j.util.om.monitor.ProgressDistributor; |
| |
| import java.sql.Connection; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.sql.Statement; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| import java.util.Timer; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public class DBStore extends Store implements IDBStore, IMappingConstants, CDOAllRevisionsProvider |
| { |
| public static final String TYPE = "db"; //$NON-NLS-1$ |
| |
| public static final int SCHEMA_VERSION = 4; |
| |
| private static final int FIRST_START = -1; |
| |
| private static final String PROP_SCHEMA_VERSION = "org.eclipse.emf.cdo.server.db.schemaVersion"; //$NON-NLS-1$ |
| |
| private static final String PROP_REPOSITORY_CREATED = "org.eclipse.emf.cdo.server.db.repositoryCreated"; //$NON-NLS-1$ |
| |
| private static final String PROP_REPOSITORY_STOPPED = "org.eclipse.emf.cdo.server.db.repositoryStopped"; //$NON-NLS-1$ |
| |
| private static final String PROP_NEXT_LOCAL_CDOID = "org.eclipse.emf.cdo.server.db.nextLocalCDOID"; //$NON-NLS-1$ |
| |
| private static final String PROP_LAST_CDOID = "org.eclipse.emf.cdo.server.db.lastCDOID"; //$NON-NLS-1$ |
| |
| private static final String PROP_LAST_BRANCHID = "org.eclipse.emf.cdo.server.db.lastBranchID"; //$NON-NLS-1$ |
| |
| private static final String PROP_LAST_LOCAL_BRANCHID = "org.eclipse.emf.cdo.server.db.lastLocalBranchID"; //$NON-NLS-1$ |
| |
| private static final String PROP_LAST_COMMITTIME = "org.eclipse.emf.cdo.server.db.lastCommitTime"; //$NON-NLS-1$ |
| |
| private static final String PROP_LAST_NONLOCAL_COMMITTIME = "org.eclipse.emf.cdo.server.db.lastNonLocalCommitTime"; //$NON-NLS-1$ |
| |
| private static final String PROP_GRACEFULLY_SHUT_DOWN = "org.eclipse.emf.cdo.server.db.gracefullyShutDown"; //$NON-NLS-1$ |
| |
| private long creationTime; |
| |
| private boolean firstTime; |
| |
| private Map<String, String> properties; |
| |
| private int idColumnLength = IDBField.DEFAULT; |
| |
| private IIDHandler idHandler; |
| |
| private IMetaDataManager metaDataManager = new MetaDataManager(this); |
| |
| private DurableLockingManager durableLockingManager = new DurableLockingManager(this); |
| |
| private IMappingStrategy mappingStrategy; |
| |
| private IDBDatabase database; |
| |
| private IDBAdapter dbAdapter; |
| |
| private IDBConnectionProvider dbConnectionProvider; |
| |
| @ExcludeFromDump |
| private transient ProgressDistributor accessorWriteDistributor = new ProgressDistributor.Geometric() |
| { |
| @Override |
| public String toString() |
| { |
| String result = "accessorWriteDistributor"; //$NON-NLS-1$ |
| if (getRepository() != null) |
| { |
| result += ": " + getRepository().getName(); //$NON-NLS-1$ |
| } |
| |
| return result; |
| } |
| }; |
| |
| @ExcludeFromDump |
| private transient StoreAccessorPool readerPool = new StoreAccessorPool(this, null); |
| |
| @ExcludeFromDump |
| private transient StoreAccessorPool writerPool = new StoreAccessorPool(this, null); |
| |
| @ExcludeFromDump |
| private transient Timer connectionKeepAliveTimer; |
| |
| public DBStore() |
| { |
| super(TYPE, null, set(ChangeFormat.REVISION, ChangeFormat.DELTA), // |
| set(RevisionTemporality.AUDITING, RevisionTemporality.NONE), // |
| set(RevisionParallelism.NONE, RevisionParallelism.BRANCHING)); |
| } |
| |
| public IMappingStrategy getMappingStrategy() |
| { |
| return mappingStrategy; |
| } |
| |
| public void setMappingStrategy(IMappingStrategy mappingStrategy) |
| { |
| this.mappingStrategy = mappingStrategy; |
| mappingStrategy.setStore(this); |
| } |
| |
| public IDBAdapter getDBAdapter() |
| { |
| return dbAdapter; |
| } |
| |
| public void setDBAdapter(IDBAdapter dbAdapter) |
| { |
| this.dbAdapter = dbAdapter; |
| } |
| |
| public void setProperties(Map<String, String> properties) |
| { |
| checkInactive(); |
| this.properties = properties; |
| } |
| |
| public Map<String, String> getProperties() |
| { |
| return properties; |
| } |
| |
| public int getIDColumnLength() |
| { |
| return idColumnLength; |
| } |
| |
| public IIDHandler getIDHandler() |
| { |
| return idHandler; |
| } |
| |
| public IDBDatabase getDatabase() |
| { |
| return database; |
| } |
| |
| public Connection getConnection() |
| { |
| Connection connection = dbConnectionProvider.getConnection(); |
| if (connection == null) |
| { |
| throw new DBException("No connection from connection provider: " + dbConnectionProvider); //$NON-NLS-1$ |
| } |
| |
| try |
| { |
| connection.setAutoCommit(false); |
| } |
| catch (SQLException ex) |
| { |
| throw new DBException(ex, "SET AUTO COMMIT = false"); |
| } |
| |
| return connection; |
| } |
| |
| public void setDBConnectionProvider(IDBConnectionProvider dbConnectionProvider) |
| { |
| this.dbConnectionProvider = dbConnectionProvider; |
| } |
| |
| public IMetaDataManager getMetaDataManager() |
| { |
| return metaDataManager; |
| } |
| |
| public DurableLockingManager getDurableLockingManager() |
| { |
| return durableLockingManager; |
| } |
| |
| public Timer getConnectionKeepAliveTimer() |
| { |
| return connectionKeepAliveTimer; |
| } |
| |
| @Override |
| public Set<ChangeFormat> getSupportedChangeFormats() |
| { |
| if (mappingStrategy.hasDeltaSupport()) |
| { |
| return set(ChangeFormat.DELTA); |
| } |
| |
| return set(ChangeFormat.REVISION); |
| } |
| |
| public ProgressDistributor getAccessorWriteDistributor() |
| { |
| return accessorWriteDistributor; |
| } |
| |
| public IDBSchema getDBSchema() |
| { |
| return database.getSchema(); |
| } |
| |
| public void visitAllTables(Connection connection, IDBStore.TableVisitor visitor) |
| { |
| for (String name : DBUtil.getAllTableNames(connection, getRepository().getName())) |
| { |
| try |
| { |
| visitor.visitTable(connection, name); |
| connection.commit(); |
| } |
| catch (SQLException ex) |
| { |
| try |
| { |
| connection.rollback(); |
| } |
| catch (SQLException ex1) |
| { |
| throw new DBException(ex1); |
| } |
| |
| if (!dbAdapter.isColumnNotFoundException(ex)) |
| { |
| throw new DBException(ex); |
| } |
| } |
| } |
| } |
| |
| public Map<String, String> getPersistentProperties(Set<String> names) |
| { |
| IDBConnection connection = database.getConnection(); |
| IDBPreparedStatement stmt = null; |
| String sql = null; |
| |
| try |
| { |
| Map<String, String> result = new HashMap<String, String>(); |
| boolean allProperties = names == null || names.isEmpty(); |
| if (allProperties) |
| { |
| sql = CDODBSchema.SQL_SELECT_ALL_PROPERTIES; |
| stmt = connection.prepareStatement(sql, ReuseProbability.MEDIUM); |
| ResultSet resultSet = null; |
| |
| try |
| { |
| resultSet = stmt.executeQuery(); |
| while (resultSet.next()) |
| { |
| String key = resultSet.getString(1); |
| String value = resultSet.getString(2); |
| result.put(key, value); |
| } |
| } |
| finally |
| { |
| DBUtil.close(resultSet); |
| } |
| } |
| else |
| { |
| sql = CDODBSchema.SQL_SELECT_PROPERTIES; |
| stmt = connection.prepareStatement(sql, ReuseProbability.MEDIUM); |
| for (String name : names) |
| { |
| stmt.setString(1, name); |
| ResultSet resultSet = null; |
| |
| try |
| { |
| resultSet = stmt.executeQuery(); |
| if (resultSet.next()) |
| { |
| String value = resultSet.getString(1); |
| result.put(name, value); |
| } |
| } |
| finally |
| { |
| DBUtil.close(resultSet); |
| } |
| } |
| } |
| |
| return result; |
| } |
| catch (SQLException ex) |
| { |
| throw new DBException(ex, sql); |
| } |
| finally |
| { |
| DBUtil.close(stmt); |
| DBUtil.close(connection); |
| } |
| } |
| |
| public void setPersistentProperties(Map<String, String> properties) |
| { |
| IDBConnection connection = database.getConnection(); |
| IDBPreparedStatement deleteStmt = connection.prepareStatement(CDODBSchema.SQL_DELETE_PROPERTIES, |
| ReuseProbability.MEDIUM); |
| IDBPreparedStatement insertStmt = connection.prepareStatement(CDODBSchema.SQL_INSERT_PROPERTIES, |
| ReuseProbability.MEDIUM); |
| String sql = null; |
| |
| try |
| { |
| for (Entry<String, String> entry : properties.entrySet()) |
| { |
| String name = entry.getKey(); |
| String value = entry.getValue(); |
| |
| sql = CDODBSchema.SQL_DELETE_PROPERTIES; |
| deleteStmt.setString(1, name); |
| deleteStmt.executeUpdate(); |
| |
| sql = CDODBSchema.SQL_INSERT_PROPERTIES; |
| insertStmt.setString(1, name); |
| insertStmt.setString(2, value); |
| insertStmt.executeUpdate(); |
| } |
| |
| sql = null; |
| connection.commit(); |
| } |
| catch (SQLException ex) |
| { |
| throw new DBException(ex, sql); |
| } |
| finally |
| { |
| DBUtil.close(insertStmt); |
| DBUtil.close(deleteStmt); |
| DBUtil.close(connection); |
| } |
| } |
| |
| public void putPersistentProperty(String key, String value) |
| { |
| Map<String, String> map = new HashMap<String, String>(); |
| map.put(key, value); |
| |
| setPersistentProperties(map); |
| } |
| |
| public void removePersistentProperties(Set<String> names) |
| { |
| IDBConnection connection = database.getConnection(); |
| IDBPreparedStatement stmt = connection.prepareStatement(CDODBSchema.SQL_DELETE_PROPERTIES, ReuseProbability.MEDIUM); |
| |
| try |
| { |
| for (String name : names) |
| { |
| stmt.setString(1, name); |
| stmt.executeUpdate(); |
| } |
| |
| connection.commit(); |
| } |
| catch (SQLException ex) |
| { |
| throw new DBException(ex, CDODBSchema.SQL_DELETE_PROPERTIES); |
| } |
| finally |
| { |
| DBUtil.close(stmt); |
| DBUtil.close(connection); |
| } |
| } |
| |
| @Override |
| public DBStoreAccessor getReader(ISession session) |
| { |
| return (DBStoreAccessor)super.getReader(session); |
| } |
| |
| @Override |
| public DBStoreAccessor getWriter(ITransaction transaction) |
| { |
| return (DBStoreAccessor)super.getWriter(transaction); |
| } |
| |
| @Override |
| protected StoreAccessorPool getReaderPool(ISession session, boolean forReleasing) |
| { |
| return readerPool; |
| } |
| |
| @Override |
| protected StoreAccessorPool getWriterPool(IView view, boolean forReleasing) |
| { |
| return writerPool; |
| } |
| |
| @Override |
| protected DBStoreAccessor createReader(ISession session) throws DBException |
| { |
| return new DBStoreAccessor(this, session); |
| } |
| |
| @Override |
| protected DBStoreAccessor createWriter(ITransaction transaction) throws DBException |
| { |
| return new DBStoreAccessor(this, transaction); |
| } |
| |
| public Map<CDOBranch, List<CDORevision>> getAllRevisions() |
| { |
| final Map<CDOBranch, List<CDORevision>> result = new HashMap<CDOBranch, List<CDORevision>>(); |
| |
| InternalSession session = null; |
| if (!StoreThreadLocal.hasSession()) |
| { |
| session = getRepository().getSessionManager().openSession(null); |
| StoreThreadLocal.setSession(session); |
| } |
| |
| try |
| { |
| StoreThreadLocal.getAccessor().handleRevisions(null, null, CDOBranchPoint.UNSPECIFIED_DATE, true, |
| new CDORevisionHandler.Filtered.Undetached(new CDORevisionHandler() |
| { |
| public boolean handleRevision(CDORevision revision) |
| { |
| CDOBranch branch = revision.getBranch(); |
| List<CDORevision> list = result.get(branch); |
| if (list == null) |
| { |
| list = new ArrayList<CDORevision>(); |
| result.put(branch, list); |
| } |
| |
| list.add(revision); |
| return true; |
| } |
| })); |
| } |
| finally |
| { |
| if (session != null) |
| { |
| StoreThreadLocal.release(); |
| session.close(); |
| } |
| } |
| |
| return result; |
| } |
| |
| public CDOID createObjectID(String val) |
| { |
| return idHandler.createCDOID(val); |
| } |
| |
| @Deprecated |
| public boolean isLocal(CDOID id) |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public CDOID getNextCDOID(LongIDStoreAccessor accessor, CDORevision revision) |
| { |
| return idHandler.getNextCDOID(revision); |
| } |
| |
| public long getCreationTime() |
| { |
| return creationTime; |
| } |
| |
| public void setCreationTime(long creationTime) |
| { |
| this.creationTime = creationTime; |
| |
| Map<String, String> map = new HashMap<String, String>(); |
| map.put(PROP_REPOSITORY_CREATED, Long.toString(creationTime)); |
| setPersistentProperties(map); |
| } |
| |
| public boolean isFirstStart() |
| { |
| return firstTime; |
| } |
| |
| @Override |
| protected void doBeforeActivate() throws Exception |
| { |
| super.doBeforeActivate(); |
| checkNull(mappingStrategy, Messages.getString("DBStore.2")); //$NON-NLS-1$ |
| checkNull(dbAdapter, Messages.getString("DBStore.1")); //$NON-NLS-1$ |
| checkNull(dbConnectionProvider, Messages.getString("DBStore.0")); //$NON-NLS-1$ |
| } |
| |
| @Override |
| protected void doActivate() throws Exception |
| { |
| super.doActivate(); |
| |
| InternalRepository repository = getRepository(); |
| IDGenerationLocation idGenerationLocation = repository.getIDGenerationLocation(); |
| if (idGenerationLocation == IDGenerationLocation.CLIENT) |
| { |
| idHandler = new UUIDHandler(this); |
| } |
| else |
| { |
| idHandler = new LongIDHandler(this); |
| } |
| |
| setObjectIDTypes(idHandler.getObjectIDTypes()); |
| connectionKeepAliveTimer = new Timer("Connection-Keep-Alive-" + this); //$NON-NLS-1$ |
| |
| if (properties != null) |
| { |
| if (idGenerationLocation == IDGenerationLocation.CLIENT) |
| { |
| String prop = properties.get(IDBStore.Props.ID_COLUMN_LENGTH); |
| if (prop != null) |
| { |
| idColumnLength = Integer.parseInt(prop); |
| } |
| } |
| |
| configureAccessorPool(readerPool, IDBStore.Props.READER_POOL_CAPACITY); |
| configureAccessorPool(writerPool, IDBStore.Props.WRITER_POOL_CAPACITY); |
| } |
| |
| Connection connection = getConnection(); |
| int schemaVersion; |
| |
| try |
| { |
| if (isDropAllDataOnActivate()) |
| { |
| OM.LOG.info("Dropping all tables from repository " + repository.getName() + "..."); |
| DBUtil.dropAllTables(connection, null); |
| connection.commit(); |
| } |
| |
| schemaVersion = selectSchemaVersion(connection); |
| if (0 <= schemaVersion && schemaVersion < SCHEMA_VERSION) |
| { |
| migrateSchema(schemaVersion); |
| } |
| |
| // CDODBSchema.INSTANCE.create(dbAdapter, connection); |
| connection.commit(); |
| } |
| finally |
| { |
| DBUtil.close(connection); |
| } |
| |
| String schemaName = repository.getName(); |
| boolean fixNullableIndexColumns = schemaVersion != FIRST_START |
| && schemaVersion < FIRST_VERSION_WITH_NULLABLE_CHECKS; |
| |
| database = DBUtil.openDatabase(dbAdapter, dbConnectionProvider, schemaName, fixNullableIndexColumns); |
| IDBSchemaTransaction schemaTransaction = database.openSchemaTransaction(); |
| |
| try |
| { |
| schemaTransaction.ensureSchema(CDODBSchema.INSTANCE); |
| schemaTransaction.commit(); |
| } |
| finally |
| { |
| schemaTransaction.close(); |
| } |
| |
| LifecycleUtil.activate(idHandler); |
| LifecycleUtil.activate(metaDataManager); |
| LifecycleUtil.activate(durableLockingManager); |
| LifecycleUtil.activate(mappingStrategy); |
| |
| setRevisionTemporality(mappingStrategy.hasAuditSupport() ? RevisionTemporality.AUDITING : RevisionTemporality.NONE); |
| setRevisionParallelism( |
| mappingStrategy.hasBranchingSupport() ? RevisionParallelism.BRANCHING : RevisionParallelism.NONE); |
| |
| if (schemaVersion == FIRST_START) |
| { |
| firstStart(); |
| } |
| else |
| { |
| reStart(); |
| } |
| |
| putPersistentProperty(PROP_SCHEMA_VERSION, Integer.toString(SCHEMA_VERSION)); |
| } |
| |
| @Override |
| protected void doDeactivate() throws Exception |
| { |
| LifecycleUtil.deactivate(mappingStrategy); |
| LifecycleUtil.deactivate(durableLockingManager); |
| LifecycleUtil.deactivate(metaDataManager); |
| LifecycleUtil.deactivate(idHandler); |
| |
| Map<String, String> map = new HashMap<String, String>(); |
| map.put(PROP_GRACEFULLY_SHUT_DOWN, Boolean.TRUE.toString()); |
| map.put(PROP_REPOSITORY_STOPPED, Long.toString(getRepository().getTimeStamp())); |
| |
| if (getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE) |
| { |
| map.put(PROP_NEXT_LOCAL_CDOID, Store.idToString(idHandler.getNextLocalObjectID())); |
| map.put(PROP_LAST_CDOID, Store.idToString(idHandler.getLastObjectID())); |
| } |
| |
| map.put(PROP_LAST_BRANCHID, Integer.toString(getLastBranchID())); |
| map.put(PROP_LAST_LOCAL_BRANCHID, Integer.toString(getLastLocalBranchID())); |
| map.put(PROP_LAST_COMMITTIME, Long.toString(getLastCommitTime())); |
| map.put(PROP_LAST_NONLOCAL_COMMITTIME, Long.toString(getLastNonLocalCommitTime())); |
| setPersistentProperties(map); |
| |
| if (readerPool != null) |
| { |
| readerPool.dispose(); |
| } |
| |
| if (writerPool != null) |
| { |
| writerPool.dispose(); |
| } |
| |
| connectionKeepAliveTimer.cancel(); |
| connectionKeepAliveTimer = null; |
| |
| super.doDeactivate(); |
| } |
| |
| protected boolean isFirstStart(Set<IDBTable> createdTables) |
| { |
| if (createdTables.contains(CDODBSchema.PROPERTIES)) |
| { |
| return true; |
| } |
| |
| Set<String> names = new HashSet<String>(); |
| names.add(PROP_REPOSITORY_CREATED); |
| |
| Map<String, String> map = getPersistentProperties(names); |
| return map.get(PROP_REPOSITORY_CREATED) == null; |
| } |
| |
| protected void firstStart() |
| { |
| InternalRepository repository = getRepository(); |
| setCreationTime(repository.getTimeStamp()); |
| firstTime = true; |
| } |
| |
| protected void reStart() throws Exception |
| { |
| Set<String> names = new HashSet<String>(); |
| names.add(PROP_REPOSITORY_CREATED); |
| names.add(PROP_GRACEFULLY_SHUT_DOWN); |
| |
| Map<String, String> map = getPersistentProperties(names); |
| creationTime = Long.valueOf(map.get(PROP_REPOSITORY_CREATED)); |
| |
| if (map.containsKey(PROP_GRACEFULLY_SHUT_DOWN)) |
| { |
| names.clear(); |
| |
| InternalRepository repository = getRepository(); |
| boolean generatingIDs = repository.getIDGenerationLocation() == IDGenerationLocation.STORE; |
| if (generatingIDs) |
| { |
| names.add(PROP_NEXT_LOCAL_CDOID); |
| names.add(PROP_LAST_CDOID); |
| } |
| |
| names.add(PROP_LAST_BRANCHID); |
| names.add(PROP_LAST_LOCAL_BRANCHID); |
| names.add(PROP_LAST_COMMITTIME); |
| names.add(PROP_LAST_NONLOCAL_COMMITTIME); |
| map = getPersistentProperties(names); |
| |
| if (generatingIDs) |
| { |
| idHandler.setNextLocalObjectID(Store.stringToID(map.get(PROP_NEXT_LOCAL_CDOID))); |
| idHandler.setLastObjectID(Store.stringToID(map.get(PROP_LAST_CDOID))); |
| } |
| |
| setLastBranchID(Integer.valueOf(map.get(PROP_LAST_BRANCHID))); |
| setLastLocalBranchID(Integer.valueOf(map.get(PROP_LAST_LOCAL_BRANCHID))); |
| setLastCommitTime(Long.valueOf(map.get(PROP_LAST_COMMITTIME))); |
| setLastNonLocalCommitTime(Long.valueOf(map.get(PROP_LAST_NONLOCAL_COMMITTIME))); |
| } |
| else |
| { |
| repairAfterCrash(); |
| } |
| |
| removePersistentProperties(Collections.singleton(PROP_GRACEFULLY_SHUT_DOWN)); |
| } |
| |
| protected void repairAfterCrash() |
| { |
| String name = getRepository().getName(); |
| OM.LOG.warn(MessageFormat.format(Messages.getString("DBStore.9"), name)); //$NON-NLS-1$ |
| |
| Connection connection = getConnection(); |
| |
| try |
| { |
| connection.setAutoCommit(false); |
| connection.setReadOnly(true); |
| |
| mappingStrategy.repairAfterCrash(dbAdapter, connection); // Must update the idHandler |
| |
| boolean storeIDs = getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE; |
| CDOID lastObjectID = storeIDs ? idHandler.getLastObjectID() : CDOID.NULL; |
| CDOID nextLocalObjectID = storeIDs ? idHandler.getNextLocalObjectID() : CDOID.NULL; |
| |
| int branchID = DBUtil.selectMaximumInt(connection, CDODBSchema.BRANCHES_ID); |
| setLastBranchID(branchID > 0 ? branchID : 0); |
| |
| int localBranchID = DBUtil.selectMinimumInt(connection, CDODBSchema.BRANCHES_ID); |
| setLastLocalBranchID(localBranchID < 0 ? localBranchID : 0); |
| |
| long lastCommitTime = DBUtil.selectMaximumLong(connection, CDODBSchema.COMMIT_INFOS_TIMESTAMP); |
| setLastCommitTime(lastCommitTime); |
| |
| long lastNonLocalCommitTime = DBUtil.selectMaximumLong(connection, CDODBSchema.COMMIT_INFOS_TIMESTAMP, |
| CDOBranch.MAIN_BRANCH_ID + "<=" + CDODBSchema.COMMIT_INFOS_BRANCH); |
| setLastNonLocalCommitTime(lastNonLocalCommitTime); |
| |
| if (storeIDs) |
| { |
| OM.LOG.info(MessageFormat.format(Messages.getString("DBStore.10"), name, lastObjectID, nextLocalObjectID, //$NON-NLS-1$ |
| getLastBranchID(), getLastCommitTime(), getLastNonLocalCommitTime())); |
| } |
| else |
| { |
| OM.LOG.info(MessageFormat.format(Messages.getString("DBStore.10b"), name, getLastBranchID(), //$NON-NLS-1$ |
| getLastCommitTime(), getLastNonLocalCommitTime())); |
| } |
| } |
| catch (SQLException e) |
| { |
| OM.LOG.error(MessageFormat.format(Messages.getString("DBStore.11"), name), e); //$NON-NLS-1$ |
| throw new DBException(e); |
| } |
| finally |
| { |
| DBUtil.close(connection); |
| } |
| } |
| |
| protected void configureAccessorPool(StoreAccessorPool pool, String property) |
| { |
| if (pool != null) |
| { |
| String value = properties.get(property); |
| if (value != null) |
| { |
| int capacity = Integer.parseInt(value); |
| pool.setCapacity(capacity); |
| } |
| } |
| } |
| |
| protected int selectSchemaVersion(Connection connection) throws SQLException |
| { |
| Statement statement = null; |
| ResultSet resultSet = null; |
| |
| try |
| { |
| statement = connection.createStatement(); |
| resultSet = statement.executeQuery("SELECT " + CDODBSchema.PROPERTIES_VALUE + " FROM " + CDODBSchema.PROPERTIES |
| + " WHERE " + CDODBSchema.PROPERTIES_NAME + "='" + PROP_SCHEMA_VERSION + "'"); |
| |
| if (resultSet.next()) |
| { |
| String value = resultSet.getString(1); |
| return Integer.parseInt(value); |
| } |
| |
| return 0; |
| } |
| catch (SQLException ex) |
| { |
| connection.rollback(); |
| if (dbAdapter.isTableNotFoundException(ex)) |
| { |
| return FIRST_START; |
| } |
| |
| throw ex; |
| } |
| finally |
| { |
| DBUtil.close(resultSet); |
| DBUtil.close(statement); |
| } |
| } |
| |
| protected void migrateSchema(int fromVersion) throws Exception |
| { |
| Connection connection = null; |
| |
| try |
| { |
| connection = getConnection(); |
| |
| for (int version = fromVersion; version < SCHEMA_VERSION; version++) |
| { |
| if (SCHEMA_MIGRATORS[version] != null) |
| { |
| int nextVersion = version + 1; |
| OM.LOG.info("Migrating schema from version " + version + " to version " + nextVersion + "..."); |
| SCHEMA_MIGRATORS[version].migrateSchema(this, connection); |
| } |
| } |
| |
| connection.commit(); |
| } |
| finally |
| { |
| DBUtil.close(connection); |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private static abstract class SchemaMigrator |
| { |
| public abstract void migrateSchema(DBStore store, Connection connection) throws Exception; |
| } |
| |
| private static final int FIRST_VERSION_WITH_NULLABLE_CHECKS = 4; |
| |
| private static final SchemaMigrator NO_MIGRATION_NEEDED = null; |
| |
| private static final SchemaMigrator NON_AUDIT_MIGRATION = new SchemaMigrator() |
| { |
| @Override |
| public void migrateSchema(DBStore store, Connection connection) throws Exception |
| { |
| InternalRepository repository = store.getRepository(); |
| if (!repository.isSupportingAudits()) |
| { |
| store.visitAllTables(connection, new IDBStore.TableVisitor() |
| { |
| public void visitTable(Connection connection, String name) throws SQLException |
| { |
| Statement statement = null; |
| |
| try |
| { |
| statement = connection.createStatement(); |
| |
| String from = " FROM " + name + " WHERE " + ATTRIBUTES_VERSION + "<" + CDOBranchVersion.FIRST_VERSION; |
| |
| statement.executeUpdate("DELETE FROM " + CDODBSchema.CDO_OBJECTS + " WHERE " + ATTRIBUTES_ID |
| + " IN (SELECT " + ATTRIBUTES_ID + from + ")"); |
| |
| statement.executeUpdate("DELETE" + from); |
| } |
| finally |
| { |
| DBUtil.close(statement); |
| } |
| } |
| }); |
| } |
| } |
| }; |
| |
| private static final SchemaMigrator LOB_SIZE_MIGRATION = new SchemaMigrator() |
| { |
| @Override |
| public void migrateSchema(DBStore store, final Connection connection) throws Exception |
| { |
| Statement statement = null; |
| |
| try |
| { |
| statement = connection.createStatement(); |
| |
| IDBAdapter dbAdapter = store.getDBAdapter(); |
| String sql = dbAdapter.sqlRenameField(CDODBSchema.LOBS_SIZE, "size"); |
| statement.execute(sql); |
| } |
| finally |
| { |
| DBUtil.close(statement); |
| } |
| } |
| }; |
| |
| private static final SchemaMigrator[] SCHEMA_MIGRATORS = { NO_MIGRATION_NEEDED, NON_AUDIT_MIGRATION, |
| LOB_SIZE_MIGRATION, NO_MIGRATION_NEEDED }; |
| |
| static |
| { |
| if (SCHEMA_MIGRATORS.length != SCHEMA_VERSION) |
| { |
| throw new Error("There must be exactly " + SCHEMA_VERSION + " schema migrators provided"); |
| } |
| } |
| } |