/*
* Copyright (c) 2012, 2016 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
*/
package org.eclipse.emf.cdo.server.internal.lissome.db;
import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation;
import org.eclipse.emf.cdo.common.branch.CDOBranch;
import org.eclipse.emf.cdo.common.branch.CDOBranchHandler;
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.server.IStoreAccessor;
import org.eclipse.emf.cdo.server.internal.lissome.LissomeStore;
import org.eclipse.emf.cdo.server.internal.lissome.bundle.OM;
import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch;
import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager;
import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager.BranchLoader.BranchInfo;
import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager.BranchLoader.SubBranchInfo;
import org.eclipse.emf.cdo.util.CDOURIUtil;
import org.eclipse.net4j.db.DBException;
import org.eclipse.net4j.db.DBUtil;
import org.eclipse.net4j.db.IDBConnectionProvider;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import org.eclipse.emf.ecore.EClass;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* @author Eike Stepper
*/
public class IndexReader implements IDBConnectionProvider
{
private static final ContextTracer TRACER = new ContextTracer(OM.INDEX, IndexReader.class);
protected Index index;
protected IDGenerationLocation idGenerationLocation;
protected boolean supportingAudits;
protected boolean supportingBranches;
protected Connection connection;
protected PreparedStatement[] queryResourcesStatements = new PreparedStatement[4];
protected PreparedStatement[] readRevisionStatements = new PreparedStatement[2];
protected PreparedStatement readRevisionByVersionStatement;
protected PreparedStatement[] handleRevisionsStatements = new PreparedStatement[32];
protected PreparedStatement loadSubBranchesStatement;
protected PreparedStatement loadBranchesStatement;
protected PreparedStatement[] loadCommitInfosStatements = new PreparedStatement[8];
public IndexReader(Index index)
{
this.index = index;
idGenerationLocation = index.getIDGenerationLocation();
supportingAudits = index.isSupportingAudits();
supportingBranches = index.isSupportingBranches();
connection = index.getConnection();
}
public Index getIndex()
{
return index;
}
public LissomeStore getStore()
{
return index.getStore();
}
public Connection getConnection()
{
return connection;
}
protected int setParameters(PreparedStatement stmt, int column, CDOBranchPoint branchPoint) throws SQLException
{
if (supportingBranches)
{
int branchID = branchPoint.getBranch().getID();
stmt.setInt(++column, branchID);
}
if (supportingAudits)
{
long timeStamp = branchPoint.getTimeStamp();
if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
{
stmt.setLong(++column, timeStamp);
stmt.setLong(++column, timeStamp);
}
}
return column;
}
public void queryResources(IStoreAccessor.QueryResourcesContext context)
{
if (TRACER.isEnabled())
{
TRACER.format("queryResources: {0}", context); //$NON-NLS-1$
}
ResultSet resultSet = null;
try
{
boolean historical = context.getTimeStamp() != CDOBranchPoint.UNSPECIFIED_DATE;
boolean exactMatch = context.exactMatch();
int stmtIndex = (historical ? 0 : 1) + (exactMatch ? 0 : 2);
PreparedStatement stmt = queryResourcesStatements[stmtIndex];
if (stmt == null)
{
String sql = index.objects.sqlQueryResources(historical, exactMatch);
stmt = connection.prepareStatement(sql);
queryResourcesStatements[stmtIndex] = stmt;
}
int column = 0;
CDOID folderID = context.getFolderID();
index.setCDOID(stmt, ++column, folderID);
String name = context.getName();
if (name == null)
{
name = CDOURIUtil.SEGMENT_SEPARATOR;
}
else if (!exactMatch)
{
name += "%";
}
stmt.setString(++column, name);
setParameters(stmt, column, context);
index.trace(TRACER, stmt);
resultSet = stmt.executeQuery();
while (resultSet.next())
{
index.trace(TRACER, resultSet);
CDOID id = index.getCDOID(resultSet, 1);
if (!context.addResource(id))
{
// No more results allowed
break;
}
}
}
catch (SQLException ex)
{
throw new DBException(ex);
}
finally
{
DBUtil.close(resultSet);
}
}
protected RevisionInfo readRevision(PreparedStatement stmt) throws SQLException
{
ResultSet resultSet = null;
try
{
index.trace(TRACER, stmt);
resultSet = stmt.executeQuery();
if (resultSet.next())
{
index.trace(TRACER, resultSet);
RevisionInfo revisionInfo = new RevisionInfo();
revisionInfo.setPointer(resultSet.getLong(1));
if (supportingAudits)
{
revisionInfo.setRevised(resultSet.getLong(2));
}
else
{
revisionInfo.setRevised(CDOBranchPoint.UNSPECIFIED_DATE);
}
return revisionInfo;
}
return null;
}
finally
{
DBUtil.close(resultSet);
}
}
public RevisionInfo readRevision(CDOID id, CDOBranchPoint branchPoint)
{
if (TRACER.isEnabled())
{
TRACER.format("readRevision: {0}, {1}", id, branchPoint); //$NON-NLS-1$
}
try
{
boolean historical = branchPoint.getTimeStamp() != CDOBranchPoint.UNSPECIFIED_DATE;
int stmtIndex = historical ? 0 : 1;
PreparedStatement stmt = readRevisionStatements[stmtIndex];
if (stmt == null)
{
String sql = index.objects.sqlReadRevision(historical);
stmt = connection.prepareStatement(sql);
readRevisionStatements[stmtIndex] = stmt;
}
int column = 0;
index.setCDOID(stmt, ++column, id);
setParameters(stmt, column, branchPoint);
return readRevision(stmt);
}
catch (SQLException ex)
{
throw new DBException(ex);
}
}
public RevisionInfo readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion)
{
if (TRACER.isEnabled())
{
TRACER.format("readRevisionByVersion: {0}, {1}", id, branchVersion); //$NON-NLS-1$
}
try
{
if (readRevisionByVersionStatement == null)
{
String sql = index.objects.sqlReadRevisionByVersion();
readRevisionByVersionStatement = connection.prepareStatement(sql);
}
index.setCDOID(readRevisionByVersionStatement, 1, id);
readRevisionByVersionStatement.setInt(2, branchVersion.getBranch().getID());
readRevisionByVersionStatement.setInt(3, Math.abs(branchVersion.getVersion()));
return readRevision(readRevisionByVersionStatement);
}
catch (SQLException ex)
{
throw new DBException(ex);
}
}
public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime, RevisionInfo.Handler handler)
{
if (TRACER.isEnabled())
{
TRACER.format("handleRevisions: {0}, {1}, {2}, {3}", eClass, branch, timeStamp, exactTime); //$NON-NLS-1$
}
ResultSet resultSet = null;
try
{
boolean withClass = eClass != null;
boolean withBranch = branch != null && supportingBranches;
boolean withTime = timeStamp != CDOBranchPoint.INVALID_DATE;
boolean historical = timeStamp != CDOBranchPoint.UNSPECIFIED_DATE;
int stmtIndex = (withClass ? 0 : 1) + (withBranch ? 0 : 2) + (withTime ? 0 : 4) + (exactTime ? 0 : 8) + (historical ? 0 : 16);
PreparedStatement stmt = handleRevisionsStatements[stmtIndex];
if (stmt == null)
{
String sql = index.objects.sqlHandleRevisions(withClass, withBranch, withTime, exactTime, historical);
stmt = connection.prepareStatement(sql);
handleRevisionsStatements[stmtIndex] = stmt;
}
int column = 0;
if (withClass)
{
int cid = getStore().getMetaID(eClass);
stmt.setInt(++column, cid);
}
if (withBranch)
{
int branchID = branch.getID();
stmt.setInt(++column, branchID);
}
if (withTime)
{
if (exactTime)
{
if (historical)
{
stmt.setLong(++column, timeStamp);
}
}
else
{
if (historical)
{
stmt.setLong(++column, timeStamp);
stmt.setLong(++column, timeStamp);
}
}
}
index.trace(TRACER, stmt);
resultSet = stmt.executeQuery();
RevisionInfo revisionInfo = new RevisionInfo();
revisionInfo.setRevised(CDOBranchPoint.UNSPECIFIED_DATE);
while (resultSet.next())
{
index.trace(TRACER, resultSet);
CDOID id = index.getCDOID(resultSet, 1);
revisionInfo.setPointer(resultSet.getLong(2));
if (supportingAudits)
{
revisionInfo.setRevised(resultSet.getLong(3));
}
handler.handleRevisionInfo(id, revisionInfo);
}
}
catch (SQLException ex)
{
throw new DBException(ex);
}
finally
{
DBUtil.close(resultSet);
}
}
public BranchInfo loadBranch(int branchID)
{
// TODO: implement IndexReader.loadBranch(branchID)
throw new UnsupportedOperationException();
// return null;
}
public SubBranchInfo[] loadSubBranches(int branchID)
{
if (TRACER.isEnabled())
{
TRACER.format("loadSubBranches: {0}", branchID); //$NON-NLS-1$
}
ResultSet resultSet = null;
try
{
if (loadSubBranchesStatement == null)
{
String sql = index.branches.sqlLoadSubBranches();
loadSubBranchesStatement = connection.prepareStatement(sql);
}
loadSubBranchesStatement.setInt(1, branchID);
index.trace(TRACER, loadSubBranchesStatement);
resultSet = loadSubBranchesStatement.executeQuery();
List<SubBranchInfo> result = new ArrayList<SubBranchInfo>();
while (resultSet.next())
{
index.trace(TRACER, resultSet);
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
long baseTimeStamp = resultSet.getLong(3);
result.add(new SubBranchInfo(id, name, baseTimeStamp));
}
return result.toArray(new SubBranchInfo[result.size()]);
}
catch (SQLException ex)
{
throw new DBException(ex);
}
finally
{
DBUtil.close(resultSet);
}
}
public int loadBranches(int startID, int endID, CDOBranchHandler handler)
{
if (TRACER.isEnabled())
{
TRACER.format("loadBranches: {0}, {1}", startID, endID); //$NON-NLS-1$
}
InternalCDOBranchManager branchManager = getStore().getRepository().getBranchManager();
ResultSet resultSet = null;
try
{
if (loadBranchesStatement == null)
{
String sql = index.branches.sqlLoadBranches();
loadBranchesStatement = connection.prepareStatement(sql);
}
loadBranchesStatement.setInt(1, startID);
loadBranchesStatement.setInt(2, endID);
index.trace(TRACER, loadBranchesStatement);
resultSet = loadBranchesStatement.executeQuery();
int count = 0;
while (resultSet.next())
{
index.trace(TRACER, resultSet);
int branchID = resultSet.getInt(1);
String name = resultSet.getString(2);
int baseBranchID = resultSet.getInt(3);
long baseTimeStamp = resultSet.getLong(4);
InternalCDOBranch branch = branchManager.getBranch(branchID, new BranchInfo(name, baseBranchID, baseTimeStamp));
handler.handleBranch(branch);
++count;
}
return count;
}
catch (SQLException ex)
{
throw new DBException(ex);
}
finally
{
DBUtil.close(resultSet);
}
}
public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, PointerHandler handler)
{
if (TRACER.isEnabled())
{
TRACER.format("loadCommitInfos: {0}, {1}, {2}", branch, startTime, endTime); //$NON-NLS-1$
}
ResultSet resultSet = null;
try
{
boolean withBranch = branch != null;
boolean withStartTime = startTime != CDOBranchPoint.UNSPECIFIED_DATE;
boolean withEndTime = endTime != CDOBranchPoint.UNSPECIFIED_DATE;
int stmtIndex = (withBranch ? 0 : 1) + (withStartTime ? 0 : 2) + (withEndTime ? 0 : 4);
PreparedStatement stmt = loadCommitInfosStatements[stmtIndex];
if (stmt == null)
{
String sql = index.commitInfos.sqlLoadCommitInfos(withBranch, withStartTime, withEndTime);
stmt = connection.prepareStatement(sql);
loadCommitInfosStatements[stmtIndex] = stmt;
}
int column = 0;
if (withBranch)
{
int branchID = branch.getID();
stmt.setInt(++column, branchID);
}
if (withStartTime)
{
stmt.setLong(++column, startTime);
}
if (withEndTime)
{
stmt.setLong(++column, endTime);
}
index.trace(TRACER, stmt);
resultSet = stmt.executeQuery();
while (resultSet.next())
{
index.trace(TRACER, resultSet);
long pointer = resultSet.getLong(1);
handler.handlePointer(pointer);
}
}
catch (SQLException ex)
{
throw new DBException(ex);
}
finally
{
DBUtil.close(resultSet);
}
}
/**
* @author Eike Stepper
*/
public interface PointerHandler
{
public void handlePointer(long pointer);
}
/**
* @author Eike Stepper
*/
public static final class RevisionInfo
{
private long pointer;
private long revised;
public RevisionInfo(long pointer, long revised)
{
this.pointer = pointer;
this.revised = revised;
}
public RevisionInfo()
{
}
public long getPointer()
{
return pointer;
}
public void setPointer(long pointer)
{
this.pointer = pointer;
}
public long getRevised()
{
return revised;
}
public void setRevised(long revised)
{
this.revised = revised;
}
@Override
public String toString()
{
return "RevisionInfo[pointer=" + pointer + ", revised=" + revised + "]";
}
/**
* @author Eike Stepper
*/
public interface Handler
{
public void handleRevisionInfo(CDOID id, IndexReader.RevisionInfo info);
}
}
}