package org.tmatesoft.svn.core.internal.wc17.db;
import org.tmatesoft.sqljet.core.SqlJetException;
import org.tmatesoft.sqljet.core.SqlJetTransactionMode;
import org.tmatesoft.sqljet.core.table.ISqlJetCursor;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.internal.db.*;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.util.SVNSkel;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNEventFactory;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc17.SVNWCContext;
import org.tmatesoft.svn.core.internal.wc17.SVNWCUtils;
import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.SVNWCDbKind;
import org.tmatesoft.svn.core.internal.wc17.db.SVNWCDb.DirParsedInfo;
import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbCreateSchema;
import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbSchema;
import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbSchema.NODES__Fields;
import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbSchema.REVERT_LIST__Fields;
import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbStatements;
import org.tmatesoft.svn.core.internal.wc17.db.statement17.SvnRevertActualNodesTrigger17;
import org.tmatesoft.svn.core.wc.*;
import org.tmatesoft.svn.util.SVNLogType;
import java.io.File;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbStatementUtil.*;
public class SvnWcDbRevert extends SvnWcDbShared {
public static void revert(SVNWCDbRoot root, File localRelPath) throws SVNException {
File movedTo;
boolean movedHere;
SVNSqlJetDb sdb = root.getSDb();
SvnRevertNodesTrigger nodesTableTrigger = new SvnRevertNodesTrigger(sdb);
ISVNSqlJetTrigger actualNodesTableTrigger = root.getFormat() == ISVNWCDb.WC_FORMAT_17 ?
new SvnRevertActualNodesTrigger17(sdb) :
new SvnRevertActualNodesTrigger(sdb);
SVNSqlJetStatement stmt = sdb.getStatement(SVNWCDbStatements.SELECT_NODE_INFO);
long opDepth;
try {
stmt.bindf("is", root.getWcId(), localRelPath);
if (!stmt.next()) {
reset(stmt);
stmt = sdb.getStatement(SVNWCDbStatements.DELETE_ACTUAL_NODE);
long affectedRows;
try {
((SVNSqlJetTableStatement) stmt).addTrigger(actualNodesTableTrigger);
stmt.bindf("is", root.getWcId(), localRelPath);
affectedRows = stmt.done();
} finally {
stmt.reset();
}
if (affectedRows > 0) {
if (root.getFormat() == ISVNWCDb.WC_FORMAT_17) {
stmt = sdb.getStatement(SVNWCDbStatements.SELECT_ACTUAL_CHILDREN_INFO_17);
} else {
stmt = sdb.getStatement(SVNWCDbStatements.SELECT_ACTUAL_CHILDREN_INFO);
}
try {
stmt.bindf("is", root.getWcId(), localRelPath);
if (stmt.next()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_OPERATION_DEPTH, "Can''t revert ''{0}'' without reverting children", root.getAbsPath(localRelPath));
SVNErrorManager.error(err, SVNLogType.WC);
}
} finally {
reset(stmt);
}
return;
}
nodeNotFound(root, localRelPath);
}
opDepth = getColumnInt64(stmt, NODES__Fields.op_depth);
movedTo = getColumnPath(stmt, NODES__Fields.moved_to);
movedHere = getColumnBoolean(stmt, NODES__Fields.moved_here);
} finally {
reset(stmt);
}
if (movedTo != null) {
SVNWCDb.ResolveBreakMovedAway resolveBreakMovedAway = new SVNWCDb.ResolveBreakMovedAway();
resolveBreakMovedAway.wcRoot = root;
resolveBreakMovedAway.localRelPath = localRelPath;
try {
resolveBreakMovedAway.transaction(sdb);
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
}
} else {
SVNSkel conflict = root.getDb().readConflictInternal(root, localRelPath);
if (conflict != null) {
Structure<SvnWcDbConflicts.ConflictInfo> conflictInfoStructure = SvnWcDbConflicts.readConflictInfo(conflict);
boolean treeConflicted = conflictInfoStructure.is(SvnWcDbConflicts.ConflictInfo.treeConflicted);
SVNOperation operation = conflictInfoStructure.get(SvnWcDbConflicts.ConflictInfo.conflictOperation);
if (treeConflicted && (operation == SVNOperation.UPDATE || operation == SVNOperation.SWITCH)) {
Structure<SvnWcDbConflicts.TreeConflictInfo> treeConflictInfoStructure = SvnWcDbConflicts.readTreeConflict(root.getDb(), root.getAbsPath(), conflict);
SVNConflictReason reason = treeConflictInfoStructure.get(SvnWcDbConflicts.TreeConflictInfo.localChange);
SVNConflictAction action = treeConflictInfoStructure.get(SvnWcDbConflicts.TreeConflictInfo.incomingChange);
if (reason == SVNConflictReason.DELETED) {
root.getDb().resolveDeleteRaiseMovedAway(SVNFileUtil.createFilePath(root.getAbsPath(), localRelPath), null);
}
}
}
}
if (opDepth > 0 && opDepth == SVNWCUtils.relpathDepth(localRelPath)) {
boolean haveRow;
stmt = sdb.getStatement(SVNWCDbStatements.SELECT_GE_OP_DEPTH_CHILDREN);
try {
stmt.bindf("isi", root.getWcId(), localRelPath, opDepth);
haveRow = stmt.next();
} finally {
reset(stmt);
}
if (haveRow) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_OPERATION_DEPTH, "Can''t revert ''{0}'' without reverting children", root.getAbsPath(localRelPath));
SVNErrorManager.error(err, SVNLogType.WC);
}
if (root.getFormat() == ISVNWCDb.WC_FORMAT_17) {
stmt = sdb.getStatement(SVNWCDbStatements.SELECT_ACTUAL_CHILDREN_INFO_17);
} else {
stmt = sdb.getStatement(SVNWCDbStatements.SELECT_ACTUAL_CHILDREN_INFO);
}
try {
stmt.bindf("is", root.getWcId(), localRelPath);
haveRow = stmt.next();
} finally {
reset(stmt);
}
if (haveRow) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_OPERATION_DEPTH, "Can''t revert ''{0}'' without reverting children", root.getAbsPath(localRelPath));
SVNErrorManager.error(err, SVNLogType.WC);
}
stmt = sdb.getStatement(SVNWCDbStatements.UPDATE_OP_DEPTH_INCREASE_RECURSIVE);
try {
((SVNSqlJetTableStatement) stmt).addTrigger(nodesTableTrigger);
stmt.bindf("isi", root.getWcId(), localRelPath, opDepth);
stmt.done();
} finally {
stmt.reset();
}
stmt = sdb.getStatement(SVNWCDbStatements.DELETE_WORKING_NODE);
try {
((SVNSqlJetTableStatement) stmt).addTrigger(nodesTableTrigger);
stmt.bindf("is", root.getWcId(), localRelPath);
stmt.done();
} finally {
stmt.reset();
}
stmt = sdb.getStatement(SVNWCDbStatements.DELETE_WC_LOCK_ORPHAN);
try {
stmt.bindf("is", root.getWcId(), localRelPath);
stmt.done();
} finally {
stmt.reset();
}
if (movedHere) {
clearMovedTo(root, localRelPath, nodesTableTrigger);
}
}
long affectedRows;
stmt = sdb.getStatement(SVNWCDbStatements.DELETE_ACTUAL_NODE_LEAVING_CHANGELIST);
try {
((SVNSqlJetTableStatement) stmt).addTrigger(actualNodesTableTrigger);
stmt.bindf("is", root.getWcId(), localRelPath);
affectedRows = stmt.done();
} finally {
stmt.reset();
}
if (affectedRows == 0) {
stmt = sdb.getStatement(SVNWCDbStatements.CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST);
try {
((SVNSqlJetTableStatement) stmt).addTrigger(actualNodesTableTrigger);
stmt.bindf("is", root.getWcId(), localRelPath);
stmt.done();
} finally {
stmt.reset();
}
}
}
private static void clearMovedTo(SVNWCDbRoot root, File localRelPath, SvnRevertNodesTrigger nodesTableTrigger) throws SVNException {
SVNSqlJetStatement stmt = root.getSDb().getStatement(root.getFormat() == ISVNWCDb.WC_FORMAT_17 ? SVNWCDbStatements.SELECT_MOVED_FROM_RELPATH_17 : SVNWCDbStatements.SELECT_MOVED_FROM_RELPATH);
try {
stmt.bindf("is", root.getWcId(), localRelPath);
boolean haveRow = stmt.next();
if (!haveRow) {
return;
}
File movedFromRelPath = SvnWcDbStatementUtil.getColumnPath(stmt, NODES__Fields.local_relpath);
stmt.reset();
stmt = root.getSDb().getStatement(SVNWCDbStatements.CLEAR_MOVE_TO_RELPATH);
stmt.bindf("isi", root.getWcId(), movedFromRelPath, SVNWCUtils.relpathDepth(movedFromRelPath));
((SVNSqlJetTableStatement) stmt).addTrigger(nodesTableTrigger);
stmt.done();
} finally {
stmt.reset();
}
}
public static void revertRecursive(SVNWCDbRoot root, File localRelPath) throws SVNException {
boolean movedHere;
SVNSqlJetDb sdb = root.getSDb();
SvnRevertNodesTrigger nodesTableTrigger = new SvnRevertNodesTrigger(sdb);
ISVNSqlJetTrigger actualNodesTableTrigger = root.getFormat() == ISVNWCDb.WC_FORMAT_17 ?
new SvnRevertActualNodesTrigger17(sdb) :
new SvnRevertActualNodesTrigger(sdb);
SVNSqlJetStatement stmt = sdb.getStatement(SVNWCDbStatements.SELECT_NODE_INFO);
long opDepth;
try {
stmt.bindf("is", root.getWcId(), localRelPath);
if (!stmt.next()) {
reset(stmt);
long affectedRows;
stmt = sdb.getStatement(SVNWCDbStatements.DELETE_ACTUAL_NODE_RECURSIVE);
try {
((SVNSqlJetTableStatement) stmt).addTrigger(actualNodesTableTrigger);
stmt.bindf("is", root.getWcId(), localRelPath);
affectedRows = stmt.done();
} finally {
stmt.reset();
}
if (affectedRows > 0) {
return;
}
nodeNotFound(root, localRelPath);
}
opDepth = getColumnInt64(stmt, NODES__Fields.op_depth);
movedHere = getColumnBoolean(stmt, NODES__Fields.moved_here);
} finally {
reset(stmt);
}
if (opDepth > 0 && opDepth != SVNWCUtils.relpathDepth(localRelPath)) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_OPERATION_DEPTH, "Can't revert ''{0}'' without" + " reverting parent", SVNFileUtil.createFilePath(root.getAbsPath(), localRelPath));
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
stmt = sdb.getStatement(SVNWCDbStatements.SELECT_MOVED_OUTSIDE);
try {
stmt.bindf("isi", root.getWcId(), localRelPath, opDepth);
boolean haveRow = stmt.next();
while (haveRow) {
File moveSrcRelPath = getColumnPath(stmt, NODES__Fields.local_relpath);
SVNWCDb.ResolveBreakMovedAway resolveBreakMovedAway = new SVNWCDb.ResolveBreakMovedAway();
resolveBreakMovedAway.wcRoot = root;
resolveBreakMovedAway.localRelPath = moveSrcRelPath;
try {
resolveBreakMovedAway.transaction(sdb);
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
}
haveRow = stmt.next();
}
} finally {
reset(stmt);
}
long selectOpDepth = opDepth != 0 ? opDepth : 1;
stmt = sdb.getStatement(SVNWCDbStatements.DELETE_NODES_ABOVE_DEPTH_RECURSIVE);
try {
stmt.bindf("isi", root.getWcId(), localRelPath, selectOpDepth);
((SVNSqlJetTableStatement) stmt).addTrigger(nodesTableTrigger);
stmt.done();
} finally {
reset(stmt);
}
if (opDepth > 0 && opDepth != SVNWCUtils.relpathDepth(localRelPath)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_OPERATION_DEPTH, "Can''t revert ''{0}'' without reverting parent", root.getAbsPath(localRelPath));
SVNErrorManager.error(err, SVNLogType.WC);
}
stmt = sdb.getStatement(SVNWCDbStatements.DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE);
try {
((SVNSqlJetTableStatement) stmt).addTrigger(actualNodesTableTrigger);
stmt.bindf("is", root.getWcId(), localRelPath);
stmt.done();
} finally {
stmt.reset();
}
stmt = sdb.getStatement(SVNWCDbStatements.CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE);
try {
((SVNSqlJetTableStatement) stmt).addTrigger(actualNodesTableTrigger);
stmt.bindf("is", root.getWcId(), localRelPath);
stmt.done();
} finally {
stmt.reset();
}
stmt = sdb.getStatement(SVNWCDbStatements.DELETE_WC_LOCK_ORPHAN_RECURSIVE);
try {
stmt.bindf("is", root.getWcId(), localRelPath);
stmt.done();
} finally {
stmt.reset();
}
stmt = sdb.getStatement(SVNWCDbStatements.SELECT_MOVED_HERE_CHILDREN);
try {
stmt.bindf("is", root.getWcId(), localRelPath);
boolean haveRow = stmt.next();
while (haveRow) {
File movedHereChildRelPath = getColumnPath(stmt, NODES__Fields.moved_to);
clearMovedTo(root, movedHereChildRelPath, nodesTableTrigger);
haveRow = stmt.next();
}
} finally {
stmt.reset();
}
if (opDepth > 0 && opDepth == SVNWCUtils.relpathDepth(localRelPath) && movedHere) {
clearMovedTo(root, localRelPath, nodesTableTrigger);
}
}
public enum RevertInfo {
reverted,
conflictOld,
conflictNew,
conflictWorking,
propReject,
copiedHere,
kind,
markerFiles
}
public static Map<File, SVNWCDbKind> readRevertCopiedChildren(SVNWCContext context, File localAbsPath) throws SVNException {
Map<File, SVNWCDbKind> result = new TreeMap<File, ISVNWCDb.SVNWCDbKind>(new Comparator<File>() {
public int compare(File o1, File o2) {
String path1 = o1.getAbsolutePath();
String path2 = o2.getAbsolutePath();
return -SVNPathUtil.PATH_COMPARATOR.compare(path1, path2);
}
});
SVNWCDb db = (SVNWCDb) context.getDb();
DirParsedInfo dirInfo = db.obtainWcRoot(localAbsPath);
File localRelpath = dirInfo.localRelPath;
SVNWCDbRoot root = dirInfo.wcDbDir.getWCRoot();
root.getSDb().getTemporaryDb().beginTransaction(SqlJetTransactionMode.READ_ONLY);
SVNSqlJetStatement stmt = null;
try {
stmt = root.getSDb().getTemporaryDb().getStatement(SVNWCDbStatements.SELECT_REVERT_LIST_COPIED_CHILDREN);
stmt.bindf("si", localRelpath, SVNWCUtils.relpathDepth(localRelpath));
while(stmt.next()) {
String relpath = getColumnText(stmt, REVERT_LIST__Fields.local_relpath);
File childFile = SVNFileUtil.createFilePath(root.getAbsPath(), relpath);
result.put(childFile, getColumnKind(stmt, REVERT_LIST__Fields.kind));
}
} finally {
reset(stmt);
root.getSDb().getTemporaryDb().commit();
}
return result;
}
public static Structure<RevertInfo> readRevertInfo(SVNWCContext context, File localAbsPath) throws SVNException {
SVNWCDb db = (SVNWCDb) context.getDb();
DirParsedInfo dirInfo = db.obtainWcRoot(localAbsPath);
File localRelpath = dirInfo.localRelPath;
SVNWCDbRoot root = dirInfo.wcDbDir.getWCRoot();
root.getSDb().getTemporaryDb().beginTransaction(SqlJetTransactionMode.WRITE);
Structure<RevertInfo> result = Structure.obtain(RevertInfo.class);
result.set(RevertInfo.kind, SVNWCDbKind.Unknown);
result.set(RevertInfo.reverted, false);
result.set(RevertInfo.copiedHere, false);
try {
/**
* SELECT conflict_old, conflict_new, conflict_working, prop_reject, notify,
* actual, op_depth, repos_id, kind
* FROM revert_list
* WHERE local_relpath = ?1
* ORDER BY actual DESC
*/
SVNSqlJetStatement stmt = new SVNSqlJetSelectStatement(root.getSDb().getTemporaryDb(), SVNWCDbSchema.REVERT_LIST) {
@Override
protected ISqlJetCursor openCursor() throws SVNException {
try {
return super.openCursor().reverse();
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
}
return null;
}
@Override
protected boolean isFilterPassed() throws SVNException {
return getBind(1).equals(getColumnString(REVERT_LIST__Fields.local_relpath));
}
@Override
protected Object[] getWhere() throws SVNException {
return new Object[]{};
}
};
stmt.bindf("s", localRelpath);
boolean haveRow = stmt.next();
if (haveRow) {
boolean isActual = getColumnBoolean(stmt, REVERT_LIST__Fields.actual);
boolean anotherRow = false;
if (isActual) {
byte[] conflictData = getColumnBlob(stmt, REVERT_LIST__Fields.conflict_data);
if (conflictData != null) {
SVNSkel conflicts = SVNSkel.parse(conflictData);
result.set(RevertInfo.markerFiles, SvnWcDbConflicts.readConflictMarkers((SVNWCDb) context.getDb(), root.getAbsPath(), conflicts));
}
if (!isColumnNull(stmt, REVERT_LIST__Fields.notify)) {
result.set(RevertInfo.reverted, true);
}
anotherRow = stmt.next();
}
if (!isActual || anotherRow) {
result.set(RevertInfo.reverted, true);
if (!isColumnNull(stmt, REVERT_LIST__Fields.repos_id)) {
long opDepth = getColumnInt64(stmt, REVERT_LIST__Fields.op_depth);
result.set(RevertInfo.copiedHere, opDepth == SVNWCUtils.relpathDepth(localRelpath));
}
result.set(RevertInfo.kind, getColumnKind(stmt, REVERT_LIST__Fields.kind));
}
}
reset(stmt);
if (haveRow) {
stmt = new SVNSqlJetDeleteStatement(root.getSDb().getTemporaryDb(), SVNWCDbSchema.REVERT_LIST) {
@Override
protected boolean isFilterPassed() throws SVNException {
return getBind(1).equals(getColumnString(REVERT_LIST__Fields.local_relpath));
}
@Override
protected Object[] getWhere() throws SVNException {
return new Object[]{};
}
};
try {
stmt.bindf("s", localRelpath);
stmt.done();
} finally {
stmt.reset();
}
}
} catch (SVNException e) {
root.getSDb().getTemporaryDb().rollback();
throw e;
} finally {
root.getSDb().getTemporaryDb().commit();
}
return result;
}
public static void dropRevertList(SVNWCContext context, File localAbsPath) throws SVNException {
SVNWCDb db = (SVNWCDb) context.getDb();
DirParsedInfo dirInfo = db.obtainWcRoot(localAbsPath);
SVNWCDbRoot root = dirInfo.wcDbDir.getWCRoot();
SVNSqlJetStatement stmt = new SVNWCDbCreateSchema(root.getSDb().getTemporaryDb(), SVNWCDbCreateSchema.DROP_REVERT_LIST, -1, false);
try {
stmt.done();
} finally {
stmt.reset();
}
}
public static void notifyRevert(SVNWCContext context, File localAbsPath, ISVNEventHandler eventHandler) throws SVNException {
SVNWCDb db = (SVNWCDb) context.getDb();
DirParsedInfo dirInfo = db.obtainWcRoot(localAbsPath);
File localRelpath = dirInfo.localRelPath;
SVNWCDbRoot root = dirInfo.wcDbDir.getWCRoot();
SVNSqlJetStatement stmt = new SVNSqlJetSelectStatement(root.getSDb().getTemporaryDb(), SVNWCDbSchema.REVERT_LIST) {
@Override
protected boolean isFilterPassed() throws SVNException {
String rowPath = getColumnString(REVERT_LIST__Fields.local_relpath);
String selectPath = (String) getBind(1);
if (selectPath.equals(rowPath) || "".equals(selectPath) || rowPath.startsWith(selectPath + "/")) {
return !isColumnNull(REVERT_LIST__Fields.notify) || getColumnLong(REVERT_LIST__Fields.actual) == 0;
}
return false;
}
@Override
protected Object[] getWhere() throws SVNException {
return new Object[] {};
}
};
stmt.bindf("s", localRelpath);
try {
if (eventHandler != null) {
File previousPath = null;
while(stmt.next()) {
File notifyRelPath = getColumnPath(stmt, REVERT_LIST__Fields.local_relpath);
if (previousPath != null && notifyRelPath.equals(previousPath)) {
continue;
}
previousPath = notifyRelPath;
File notifyAbsPath = SVNFileUtil.createFilePath(root.getAbsPath(), notifyRelPath);
eventHandler.handleEvent(SVNEventFactory.createSVNEvent(notifyAbsPath, SVNNodeKind.NONE, null, -1, SVNEventAction.REVERT,
SVNEventAction.REVERT, null, null, -1, -1), -1);
}
}
} finally {
reset(stmt);
}
stmt = new SVNSqlJetDeleteStatement(root.getSDb().getTemporaryDb(), SVNWCDbSchema.REVERT_LIST) {
@Override
protected Object[] getWhere() throws SVNException {
return new Object[0];
}
@Override
protected boolean isFilterPassed() throws SVNException {
String selectPath = (String) getBind(1);
if ("".equals(selectPath)) {
return true;
}
String rowPath = getColumnString(REVERT_LIST__Fields.local_relpath);
return rowPath.equals(selectPath) || rowPath.startsWith(selectPath + "/");
}
};
try {
stmt.bindf("s", localRelpath);
stmt.done();
} finally {
stmt.reset();
}
}
}