package org.tmatesoft.svn.core.internal.wc17.db; import static org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbStatementUtil.getColumnChecksum; import static org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbStatementUtil.getColumnDate; import static org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbStatementUtil.getColumnInt64; import static org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbStatementUtil.getColumnKind; import static org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbStatementUtil.getColumnPath; import static org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbStatementUtil.getColumnPresence; import static org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbStatementUtil.getColumnProperties; import static org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbStatementUtil.getColumnText; import static org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbStatementUtil.reset; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.tmatesoft.sqljet.core.SqlJetException; import org.tmatesoft.sqljet.core.SqlJetTransactionMode; import org.tmatesoft.sqljet.core.table.ISqlJetCursor; import org.tmatesoft.sqljet.core.table.ISqlJetTable; import org.tmatesoft.sqljet.core.table.SqlJetDb; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.internal.db.SVNSqlJetDb; import org.tmatesoft.svn.core.internal.db.SVNSqlJetSelectStatement; import org.tmatesoft.svn.core.internal.db.SVNSqlJetStatement; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.core.internal.wc.SVNFileType; 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.SVNWCDbStatus; import org.tmatesoft.svn.core.internal.wc17.db.SVNWCDb.DirParsedInfo; import org.tmatesoft.svn.core.internal.wc17.db.StructureFields.WalkerChildInfo; 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.SVNWCDbStatements; import org.tmatesoft.svn.util.SVNLogType; public class SvnWcDbReader extends SvnWcDbShared { public enum ReplaceInfo { replaced, baseReplace, replaceRoot } public enum InstallInfo { wcRootAbsPath, sha1Checksum, pristineProps, changedDate, } public static Collection<File> getServerExcludedNodes(SVNWCDb db, File path) throws SVNException { DirParsedInfo dirInfo = db.obtainWcRoot(path); SVNSqlJetDb sdb = dirInfo.wcDbDir.getWCRoot().getSDb(); long wcId = dirInfo.wcDbDir.getWCRoot().getWcId(); File localRelPath = dirInfo.localRelPath; Collection<File> result = new ArrayList<File>(); SVNSqlJetStatement stmt = sdb.getStatement(SVNWCDbStatements.SELECT_ALL_SERVER_EXCLUDED_NODES); try { stmt.bindf("isi", wcId, localRelPath, 0); while(stmt.next()) { final File localPath = getColumnPath(stmt, NODES__Fields.local_relpath); final File absPath = dirInfo.wcDbDir.getWCRoot().getAbsPath(localPath); result.add(absPath); } } finally { reset(stmt); } return result; } public static Collection<File> getNotPresentDescendants(SVNWCDb db, File parentPath) throws SVNException { DirParsedInfo dirInfo = db.obtainWcRoot(parentPath); SVNSqlJetDb sdb = dirInfo.wcDbDir.getWCRoot().getSDb(); long wcId = dirInfo.wcDbDir.getWCRoot().getWcId(); File localRelPath = dirInfo.localRelPath; Collection<File> result = new ArrayList<File>(); SVNSqlJetStatement stmt = sdb.getStatement(SVNWCDbStatements.SELECT_NOT_PRESENT_DESCENDANTS); try { stmt.bindf("isi", wcId, localRelPath, SVNWCUtils.relpathDepth(localRelPath)); while(stmt.next()) { result.add(new File(SVNWCUtils.getPathAsChild(localRelPath, getColumnPath(stmt, NODES__Fields.local_relpath)))); } } finally { reset(stmt); } return result; } public static Structure<ReplaceInfo> readNodeReplaceInfo(SVNWCDb db, File localAbspath, ReplaceInfo... fields) throws SVNException { Structure<ReplaceInfo> result = Structure.obtain(ReplaceInfo.class, fields); result.set(ReplaceInfo.replaced, false); if (result.hasField(ReplaceInfo.baseReplace)) { result.set(ReplaceInfo.baseReplace, false); } if (result.hasField(ReplaceInfo.replaceRoot)) { result.set(ReplaceInfo.replaceRoot, false); } DirParsedInfo dirInfo = db.obtainWcRoot(localAbspath); SVNSqlJetDb sdb = dirInfo.wcDbDir.getWCRoot().getSDb(); long wcId = dirInfo.wcDbDir.getWCRoot().getWcId(); File localRelPath = dirInfo.localRelPath; SVNSqlJetStatement stmt = null; begingReadTransaction(dirInfo.wcDbDir.getWCRoot()); try { stmt = sdb.getStatement(SVNWCDbStatements.SELECT_NODE_INFO); stmt.bindf("is", wcId, localRelPath); if (!stmt.next()) { nodeNotFound(localAbspath); } if (getColumnPresence(stmt) != SVNWCDbStatus.Normal) { return result; } if (!stmt.next()) { return result; } SVNWCDbStatus replacedStatus = getColumnPresence(stmt); if (replacedStatus != SVNWCDbStatus.NotPresent && replacedStatus != SVNWCDbStatus.Excluded && replacedStatus != SVNWCDbStatus.ServerExcluded && replacedStatus != SVNWCDbStatus.BaseDeleted) { result.set(ReplaceInfo.replaced, true); } long replacedOpDepth = getColumnInt64(stmt, NODES__Fields.op_depth); if (result.hasField(ReplaceInfo.baseReplace)) { long opDepth = replacedOpDepth; boolean haveRow = true; while (opDepth != 0 && haveRow) { haveRow = stmt.next(); if (haveRow) { opDepth = getColumnInt64(stmt, NODES__Fields.op_depth); } } if (haveRow && opDepth == 0) { SVNWCDbStatus baseStatus = getColumnPresence(stmt); result.set(ReplaceInfo.baseReplace, baseStatus != SVNWCDbStatus.NotPresent); } } reset(stmt); if (!result.is(ReplaceInfo.replaced) || !result.hasField(ReplaceInfo.replaceRoot)) { return result; } if (replacedStatus != SVNWCDbStatus.BaseDeleted) { stmt.bindf("is", wcId, SVNFileUtil.getFileDir(localRelPath)); if (stmt.next()) { long parentOpDepth = getColumnInt64(stmt, NODES__Fields.op_depth); if (parentOpDepth >= replacedOpDepth) { result.set(ReplaceInfo.replaceRoot, parentOpDepth == replacedOpDepth); return result; } boolean haveRow = stmt.next(); if (haveRow) { parentOpDepth = getColumnInt64(stmt, NODES__Fields.op_depth); } if (!haveRow) { result.set(ReplaceInfo.replaceRoot, true); } else if (parentOpDepth < replacedOpDepth) { result.set(ReplaceInfo.replaceRoot, true); } reset(stmt); } } } finally { reset(stmt); commitTransaction(dirInfo.wcDbDir.getWCRoot()); } return result; } public static Structure<InstallInfo> readNodeInstallInfo(SVNWCDb db, File localAbspath, InstallInfo... fields) throws SVNException { final Structure<InstallInfo> result = Structure.obtain(InstallInfo.class, fields); final DirParsedInfo dirInfo = db.obtainWcRoot(localAbspath); final SVNSqlJetDb sdb = dirInfo.wcDbDir.getWCRoot().getSDb(); final long wcId = dirInfo.wcDbDir.getWCRoot().getWcId(); final File localRelPath = dirInfo.localRelPath; if (result.hasField(InstallInfo.wcRootAbsPath)) { result.set(InstallInfo.wcRootAbsPath, dirInfo.wcDbDir.getWCRoot().getAbsPath()); } begingReadTransaction(dirInfo.wcDbDir.getWCRoot()); SVNSqlJetStatement stmt = null; try { stmt = sdb.getStatement(SVNWCDbStatements.SELECT_NODE_INFO); stmt.bindf("is", wcId, localRelPath); if (!stmt.next()) { nodeIsNotInstallable(localAbspath); } else { if (result.hasField(InstallInfo.changedDate)) { result.set(InstallInfo.changedDate, getColumnDate(stmt, NODES__Fields.changed_date)); } if (result.hasField(InstallInfo.sha1Checksum)) { result.set(InstallInfo.sha1Checksum, getColumnChecksum(stmt, NODES__Fields.checksum)); } if (result.hasField(InstallInfo.pristineProps)) { result.set(InstallInfo.pristineProps, getColumnProperties(stmt, NODES__Fields.properties)); } } reset(stmt); } finally { reset(stmt); commitTransaction(dirInfo.wcDbDir.getWCRoot()); } return result; } public static long[] getMinAndMaxRevisions(SVNWCDb db, File localAbsPath) throws SVNException { DirParsedInfo dirInfo = db.obtainWcRoot(localAbsPath); SVNSqlJetDb sdb = dirInfo.wcDbDir.getWCRoot().getSDb(); final long wcId = dirInfo.wcDbDir.getWCRoot().getWcId(); final File localRelpath = dirInfo.localRelPath; final long[] revs = new long[] { -1, -1, -1, -1 }; SVNSqlJetSelectStatement stmt = new SVNSqlJetSelectStatement(sdb, SVNWCDbSchema.NODES) { @Override protected Object[] getWhere() throws SVNException { return new Object[] {wcId}; } @Override protected boolean isFilterPassed() throws SVNException { String path = getColumnString(SVNWCDbSchema.NODES__Fields.local_relpath); if ("".equals(localRelpath.getPath()) || path.equals(localRelpath.getPath()) || path.startsWith(localRelpath.getPath() + "/")) { long depth = getColumnLong(SVNWCDbSchema.NODES__Fields.op_depth); if (depth != 0) { return false; } String presence = getColumnString(SVNWCDbSchema.NODES__Fields.presence); if (!("normal".equals(presence) || "incomplete".equals(presence))) { return false; } long rev = getColumnLong(SVNWCDbSchema.NODES__Fields.revision); long changedRev = getColumnLong(SVNWCDbSchema.NODES__Fields.revision); if (getColumnBoolean(SVNWCDbSchema.NODES__Fields.file_external)) { return false; } if (revs[0] < 0 || revs[0] > rev) { revs[0] = rev; } if (revs[1] < 0 || revs[1] < rev) { revs[1] = rev; } if (revs[2] < 0 || revs[2] > changedRev) { revs[2] = changedRev; } if (revs[3] < 0 || revs[3] < changedRev) { revs[3] = changedRev; } } return false; } }; try { while(stmt.next()) {} } finally { reset(stmt); } return revs; } public static Map<String, Structure<WalkerChildInfo>> readWalkerChildrenInfo(SVNWCDb db, File localAbspath, Map<String, Structure<WalkerChildInfo>> children) throws SVNException { DirParsedInfo dirInfo = db.obtainWcRoot(localAbspath); SVNSqlJetDb sdb = dirInfo.wcDbDir.getWCRoot().getSDb(); long wcId = dirInfo.wcDbDir.getWCRoot().getWcId(); SVNSqlJetStatement stmt = sdb.getStatement(SVNWCDbStatements.SELECT_NODE_CHILDREN_WALKER_INFO); if (children == null) { children = new HashMap<String, Structure<WalkerChildInfo>>(); } try { stmt.bindf("is", wcId, dirInfo.localRelPath); while(stmt.next()) { File childPath = SVNFileUtil.createFilePath(getColumnText(stmt, NODES__Fields.local_relpath)); String childName = SVNFileUtil.getFileName(childPath); Structure<WalkerChildInfo> childInfo = children.get(childName); if (childInfo == null) { childInfo = Structure.obtain(WalkerChildInfo.class); children.put(childName, childInfo); } long opDepth = getColumnInt64(stmt, NODES__Fields.op_depth); if (opDepth > 0) { childInfo.set(WalkerChildInfo.status, SVNWCDb.getWorkingStatus(getColumnPresence(stmt))); } else { childInfo.set(WalkerChildInfo.status, getColumnPresence(stmt)); } childInfo.set(WalkerChildInfo.kind, getColumnKind(stmt, NODES__Fields.kind)); } } finally { reset(stmt); } return children; } public static boolean hasSwitchedSubtrees(SVNWCDb db, File localAbspath) throws SVNException { DirParsedInfo dirInfo = db.obtainWcRoot(localAbspath); SVNSqlJetDb sdb = dirInfo.wcDbDir.getWCRoot().getSDb(); long wcId = dirInfo.wcDbDir.getWCRoot().getWcId(); String localRelPathStr = dirInfo.localRelPath.getPath().replace(File.separatorChar, '/'); SqlJetDb sqljetDb = sdb.getDb(); String parentReposRelpath = ""; ISqlJetCursor cursor = null; try { sqljetDb.beginTransaction(SqlJetTransactionMode.READ_ONLY); ISqlJetTable nodesTable = sqljetDb.getTable(SVNWCDbSchema.NODES.toString()); String parentRelPath = null; Map<String, String> parents = new HashMap<String, String>(); cursor = nodesTable.scope(null, new Object[] {wcId, localRelPathStr}, null); if ("".equals(localRelPathStr)) { if (!cursor.eof()) { parentReposRelpath = cursor.getString(SVNWCDbSchema.NODES__Fields.repos_path.toString()); parents.put("", parentReposRelpath); cursor.next(); } } else if (!"".equals(localRelPathStr)) { parentRelPath = localRelPathStr; } boolean matched = false; while(!cursor.eof()) { String rowRelPath = cursor.getString(SVNWCDbSchema.NODES__Fields.local_relpath.toString()); boolean fileExternal = cursor.getBoolean(SVNWCDbSchema.NODES__Fields.file_external.toString()); if (fileExternal) { } else if (rowRelPath.equals(parentRelPath)) { long opDepth = cursor.getInteger(SVNWCDbSchema.NODES__Fields.op_depth.toString()); if (opDepth == 0) { parents.put(rowRelPath, cursor.getString(SVNWCDbSchema.NODES__Fields.repos_path.toString())); } } else if ("".equals(localRelPathStr) || rowRelPath.startsWith(localRelPathStr + "/")) { matched = true; long opDepth = cursor.getInteger(SVNWCDbSchema.NODES__Fields.op_depth.toString()); if (opDepth == 0) { String rowReposRelpath = cursor.getString(SVNWCDbSchema.NODES__Fields.repos_path.toString()); String rowParentRelpath = cursor.getString(SVNWCDbSchema.NODES__Fields.parent_relpath.toString()); if ("dir".equals(cursor.getString(SVNWCDbSchema.NODES__Fields.kind.toString()))) { parents.put(rowRelPath, rowReposRelpath); } parentReposRelpath = parents.get(rowParentRelpath); String expectedReposRelpath = SVNPathUtil.append(parentReposRelpath, SVNPathUtil.tail(rowRelPath)); if (!rowReposRelpath.equals(expectedReposRelpath)) { return true; } } } else if (matched) { matched = false; break; } cursor.next(); } } catch (SqlJetException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.SQLITE_ERROR, e); SVNErrorManager.error(err, SVNLogType.WC); } finally { if (cursor != null) { try { cursor.close(); } catch (SqlJetException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.SQLITE_ERROR, e); SVNErrorManager.error(err, SVNLogType.WC); } } try { sqljetDb.commit(); } catch (SqlJetException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.SQLITE_ERROR, e); SVNErrorManager.error(err, SVNLogType.WC); } } return false; } public static boolean hasLocalModifications(SVNWCContext context, File localAbspath) throws SVNException { SVNWCDb db = (SVNWCDb) context.getDb(); DirParsedInfo dirInfo = db.obtainWcRoot(localAbspath); SVNSqlJetDb sdb = dirInfo.wcDbDir.getWCRoot().getSDb(); long wcId = dirInfo.wcDbDir.getWCRoot().getWcId(); String localRelPathStr = dirInfo.localRelPath.getPath().replace(File.separatorChar, '/'); SqlJetDb sqljetDb = sdb.getDb(); ISqlJetCursor cursor = null; boolean matched = false; try { sqljetDb.beginTransaction(SqlJetTransactionMode.READ_ONLY); ISqlJetTable nodesTable = sqljetDb.getTable(SVNWCDbSchema.NODES.toString()); cursor = nodesTable.scope(null, new Object[] {wcId, localRelPathStr}, null); // tree modifications while(!cursor.eof()) { String rowRelPath = cursor.getString(SVNWCDbSchema.NODES__Fields.local_relpath.toString()); if ("".equals(localRelPathStr) || rowRelPath.equals(localRelPathStr) || rowRelPath.startsWith(localRelPathStr + "/")) { matched = true; long opDepth = cursor.getInteger(SVNWCDbSchema.NODES__Fields.op_depth.toString()); if (opDepth > 0) { return true; } } else if (matched) { matched = false; break; } cursor.next(); } cursor.close(); ISqlJetTable actualNodesTable = sqljetDb.getTable(SVNWCDbSchema.ACTUAL_NODE.toString()); // prop mods cursor = actualNodesTable.scope(null, new Object[] {wcId, localRelPathStr}, null); while(!cursor.eof()) { String rowRelPath = cursor.getString(SVNWCDbSchema.NODES__Fields.local_relpath.toString()); if ("".equals(localRelPathStr) || rowRelPath.equals(localRelPathStr) || rowRelPath.startsWith(localRelPathStr + "/")) { matched = true; if (cursor.getBlobAsArray(SVNWCDbSchema.ACTUAL_NODE__Fields.properties.toString()) != null) { return true; } } else if (matched) { matched = false; break; } cursor.next(); } cursor.close(); // text mods. cursor = nodesTable.scope(null, new Object[] {wcId, localRelPathStr}, null); while(!cursor.eof()) { String rowRelPath = cursor.getString(SVNWCDbSchema.NODES__Fields.local_relpath.toString()); if ("".equals(localRelPathStr) || rowRelPath.equals(localRelPathStr) || rowRelPath.startsWith(localRelPathStr + "/")) { matched = true; String kind = cursor.getString(SVNWCDbSchema.NODES__Fields.kind.toString()); if ("file".equals(kind)) { String presence = cursor.getString(SVNWCDbSchema.NODES__Fields.presence.toString()); if ("normal".equals(presence) && !cursor.getBoolean(SVNWCDbSchema.NODES__Fields.file_external.toString())) { File localFile = dirInfo.wcDbDir.getWCRoot().getAbsPath(new File(rowRelPath)); SVNFileType ft = SVNFileType.getType(localFile); if (!(ft == SVNFileType.FILE || ft == SVNFileType.SYMLINK)) { return true; } long size = cursor.getInteger(SVNWCDbSchema.NODES__Fields.translated_size.toString()); long date = cursor.getInteger(SVNWCDbSchema.NODES__Fields.last_mod_time.toString()); if (size != -1 && date != 0) { if (size != SVNFileUtil.getFileLength(localFile)) { return true; } if (date == SVNFileUtil.getFileLastModifiedMicros(localFile)) { cursor.next(); continue; } } if (context.isTextModified(localFile, false)) { return true; } } } } else if (matched) { matched = false; break; } cursor.next(); } cursor.close(); } catch (SqlJetException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.SQLITE_ERROR, e); SVNErrorManager.error(err, SVNLogType.WC); } finally { if (cursor != null) { try { cursor.close(); } catch (SqlJetException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.SQLITE_ERROR, e); SVNErrorManager.error(err, SVNLogType.WC); } } try { sqljetDb.commit(); } catch (SqlJetException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.SQLITE_ERROR, e); SVNErrorManager.error(err, SVNLogType.WC); } } return false; } public static boolean isSparseCheckout(SVNWCDb db, File localAbspath) throws SVNException { DirParsedInfo dirInfo = db.obtainWcRoot(localAbspath); SVNSqlJetDb sdb = dirInfo.wcDbDir.getWCRoot().getSDb(); long wcId = dirInfo.wcDbDir.getWCRoot().getWcId(); String localRelPathStr = dirInfo.localRelPath.getPath().replace(File.separatorChar, '/'); SqlJetDb sqljetDb = sdb.getDb(); ISqlJetCursor cursor = null; try { sqljetDb.beginTransaction(SqlJetTransactionMode.READ_ONLY); ISqlJetTable nodesTable = sqljetDb.getTable(SVNWCDbSchema.NODES.toString()); cursor = nodesTable.scope(null, new Object[] {wcId, localRelPathStr}, null); boolean matched = false; while(!cursor.eof()) { String rowRelPath = cursor.getString(SVNWCDbSchema.NODES__Fields.local_relpath.toString()); boolean fileExternal = cursor.getBoolean(SVNWCDbSchema.NODES__Fields.file_external.toString()); if (fileExternal) { } else if ("".equals(localRelPathStr) || rowRelPath.equals(localRelPathStr) || rowRelPath.startsWith(localRelPathStr + "/")) { matched = true; long opDepth = cursor.getInteger(SVNWCDbSchema.NODES__Fields.op_depth.toString()); if (opDepth == 0) { SVNWCDbStatus presence = SvnWcDbStatementUtil.parsePresence(cursor.getString(SVNWCDbSchema.NODES__Fields.presence.toString())); if (presence == SVNWCDbStatus.Excluded || presence == SVNWCDbStatus.ServerExcluded) { return true; } SVNDepth depth = SvnWcDbStatementUtil.parseDepth(cursor.getString(SVNWCDbSchema.NODES__Fields.depth.toString())); if (depth != SVNDepth.UNKNOWN && depth != SVNDepth.INFINITY) { return true; } } } else if (matched) { matched = false; break; } cursor.next(); } } catch (SqlJetException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.SQLITE_ERROR, e); SVNErrorManager.error(err, SVNLogType.WC); } finally { if (cursor != null) { try { cursor.close(); } catch (SqlJetException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.SQLITE_ERROR, e); SVNErrorManager.error(err, SVNLogType.WC); } } try { sqljetDb.commit(); } catch (SqlJetException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.SQLITE_ERROR, e); SVNErrorManager.error(err, SVNLogType.WC); } } return false; } }