package org.tmatesoft.svn.core.internal.wc2.ng;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
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.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.SVNProperties;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.internal.db.SVNSqlJetDb;
import org.tmatesoft.svn.core.internal.db.SVNSqlJetInsertStatement;
import org.tmatesoft.svn.core.internal.db.SVNSqlJetSelectFieldsStatement;
import org.tmatesoft.svn.core.internal.db.SVNSqlJetSelectStatement;
import org.tmatesoft.svn.core.internal.db.SVNSqlJetStatement;
import org.tmatesoft.svn.core.internal.db.SVNSqlJetUpdateStatement;
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.SVNExternal;
import org.tmatesoft.svn.core.internal.wc.SVNFileListUtil;
import org.tmatesoft.svn.core.internal.wc.SVNFileType;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc.SVNTreeConflictUtil;
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;
import org.tmatesoft.svn.core.internal.wc17.db.SVNWCDb;
import org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbConflicts;
import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbSchema;
import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbSchema.ACTUAL_NODE__Fields;
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.core.internal.wc2.old.SvnOldUpgrade;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc.SVNTreeConflictDescription;
import org.tmatesoft.svn.util.SVNLogType;
public class SvnNgUpgradeSDb {
private static String PRISTINE_STORAGE_EXT = ".svn-base";
/* Number of characters in a pristine file basename, in WC format <= 28. */
private static int PRISTINE_BASENAME_OLD_LEN = 40;
/* Return a string indicating the released version (or versions) of Subversion that used WC format number WC_FORMAT, or some other
* suitable string if no released version used WC_FORMAT.
*
* ##It's not ideal to encode this sort of knowledge in this low-level library. On the other hand, it doesn't need to be updated often and
* should be easily found when it does need to be updated. */
private static String versionStringFromFormat(int wcFormat) {
switch (wcFormat) {
case 4: return "<=1.3";
case 8: return "1.4";
case 9: return "1.5";
case 10: return "1.6";
}
return "(unreleased development version)";
}
public static int upgrade(final File wcRootAbsPath, SVNWCDb db, final SVNSqlJetDb sDb, int startFormat, ISVNEventHandler eventHandler) throws SVNException {
int resultFormat = 0;
if (startFormat < SVNWCContext.WC_NG_VERSION /* 12 */) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ILLEGAL_TARGET, "Working copy ''{0}'' is too old (format {1}, created by Subversion {2})",
wcRootAbsPath, startFormat, versionStringFromFormat(startFormat));
SVNErrorManager.error(err, SVNLogType.WC);
}
/* Early WCNG formats no longer supported. */
if (startFormat < 19) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ILLEGAL_TARGET,
"Working copy ''{0}'' is an old development version (format {1}); to upgrade it, use a format 18 client, then " +
"use ''tools/dev/wc-ng/bump-to-19.py'', then use the current client",
wcRootAbsPath, startFormat);
SVNErrorManager.error(err, SVNLogType.WC);
}
/* ##need lock-out. only one upgrade at a time. note that other code cannot use this un-upgraded database until we finish the upgrade. */
/* Note: none of these have "break" statements; the fall-through is intentional. */
switch (startFormat)
{
case 19:
runBump(db, sDb, wcRootAbsPath, new bumpTo20());
resultFormat = 20;
case 20:
runBump(db, sDb, wcRootAbsPath, new bumpTo21());
resultFormat = 21;
case 21:
runBump(db, sDb, wcRootAbsPath, new bumpTo22());
resultFormat = 22;
case 22:
runBump(db, sDb, wcRootAbsPath, new bumpTo23());
resultFormat = 23;
case 23:
runBump(db, sDb, wcRootAbsPath, new bumpTo24());
resultFormat = 24;
case 24:
runBump(db, sDb, wcRootAbsPath, new bumpTo25());
resultFormat = 25;
case 25:
runBump(db, sDb, wcRootAbsPath, new bumpTo26());
resultFormat = 26;
case 26:
runBump(db, sDb, wcRootAbsPath, new bumpTo27());
resultFormat = 27;
case 27:
runBump(db, sDb, wcRootAbsPath, new bumpTo28());
resultFormat = 28;
case 28:
runBump(db, sDb, wcRootAbsPath, new bumpTo29());
resultFormat = 29;
case 29:
runBump(db, sDb, wcRootAbsPath, new bumpTo30());
resultFormat = 30;
case 30:
runBump(db, sDb, wcRootAbsPath, new bumpTo31());
resultFormat = 31;
/* ##future bumps go here. */
//#if 0
//case XXX-1:
/* Revamp the recording of tree conflicts. */
//SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_XXX, &bb, scratch_pool));
//*result_format = XXX;
/* FALLTHROUGH */
//#endif*/
}
/*
#ifdef SVN_DEBUG
if (*result_format != start_format)
{
int schema_version;
SVN_ERR(svn_sqlite__read_schema_version(&schema_version, sdb, scratch_pool));
/* If this assertion fails the schema isn't updated correctly /
SVN_ERR_ASSERT(schema_version == *result_format);
}
#endif
*/
/* Zap anything that might be remaining or escaped our notice. */
SvnOldUpgrade.wipeObsoleteFiles(wcRootAbsPath);
if (resultFormat != 0 && eventHandler != null) {
SVNEvent event = SVNEventFactory.createSVNEvent(wcRootAbsPath, SVNNodeKind.UNKNOWN, null, SVNRepository.INVALID_REVISION, SVNEventAction.UPGRADED_PATH, SVNEventAction.UPGRADED_PATH, null, null);
eventHandler.handleEvent(event, ISVNEventHandler.UNKNOWN);
}
return resultFormat;
}
private static void migrateTreeConflictData(SVNSqlJetDb sDb) throws SVNException
{
SVNSqlJetStatement stmt = new SVNSqlJetSelectFieldsStatement<SVNWCDbSchema.ACTUAL_NODE__Fields>(sDb, SVNWCDbSchema.ACTUAL_NODE) {
protected void defineFields() {
fields.add(SVNWCDbSchema.ACTUAL_NODE__Fields.wc_id);
fields.add(SVNWCDbSchema.ACTUAL_NODE__Fields.local_relpath);
fields.add(SVNWCDbSchema.ACTUAL_NODE__Fields.tree_conflict_data);
}
protected boolean isFilterPassed() throws SVNException {
return !isColumnNull(SVNWCDbSchema.ACTUAL_NODE__Fields.tree_conflict_data);
}
};
/* Iterate over each node which has a set of tree conflicts, then insert all of them into the new schema. */
try {
while (stmt.next()) {
migrateSingleTreeConflictData(sDb,
stmt.getColumnString(SVNWCDbSchema.ACTUAL_NODE__Fields.tree_conflict_data),
stmt.getColumnLong(SVNWCDbSchema.ACTUAL_NODE__Fields.wc_id),
SVNFileUtil.createFilePath(stmt.getColumnString(SVNWCDbSchema.ACTUAL_NODE__Fields.local_relpath)));
}
} finally {
stmt.reset();
}
/* Erase all the old tree conflict data. */
stmt = new SVNSqlJetUpdateStatement(sDb, SVNWCDbSchema.ACTUAL_NODE) {
public Map<String, Object> getUpdateValues() throws SVNException {
Map<String, Object> rowValues = getRowValues();
rowValues.put(SVNWCDbSchema.ACTUAL_NODE__Fields.tree_conflict_data.toString(), null);
return rowValues;
}
};
try {
stmt.exec();
} finally {
stmt.reset();
}
}
private static void migrateSingleTreeConflictData(SVNSqlJetDb sDb, String treeConflictData, long wcId, File localRelPath) throws SVNException {
@SuppressWarnings("unchecked")
Map<File, SVNTreeConflictDescription> conflicts = SVNTreeConflictUtil.readTreeConflicts(localRelPath, treeConflictData);
for (Iterator<File> keys = conflicts.keySet().iterator(); keys.hasNext();) {
File entryPath = keys.next();
SVNTreeConflictDescription conflict = conflicts.get(entryPath);
/* See if we need to update or insert an ACTUAL node. */
SVNSqlJetStatement stmt = sDb.getStatement(SVNWCDbStatements.SELECT_ACTUAL_NODE);
stmt.bindf("is", wcId, conflict.getPath());
boolean haveRow = false;
try {
haveRow = stmt.next();
} finally {
stmt.reset();
}
if (haveRow) {
/* There is an existing ACTUAL row, so just update it. */
stmt = sDb.getStatement(SVNWCDbStatements.UPDATE_ACTUAL_CONFLICT_DATA);
} else {
/* We need to insert an ACTUAL row with the tree conflict data. */
stmt = sDb.getStatement(SVNWCDbStatements.INSERT_ACTUAL_CONFLICT_DATA);
}
stmt.bindf("iss", wcId, conflict.getPath(), SVNTreeConflictUtil.getSingleTreeConflictData(conflict));
if (!haveRow)
stmt.bindString(4, SVNFileUtil.getFilePath(localRelPath));
try {
stmt.exec();
} finally {
stmt.reset();
}
}
}
public static void setVersion(SVNSqlJetDb sDb, int version) throws SVNException {
try {
sDb.getDb().pragma("pragma user_version = " + version);
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
}
}
private interface Bumpable {
public void bumpTo(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException;
}
private static void runBump(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath, Bumpable bump) throws SVNException {
sDb.beginTransaction(SqlJetTransactionMode.WRITE);
try {
bump.bumpTo(db, sDb, wcRootAbsPath);
} catch (SVNException e) {
sDb.rollback();
throw e;
}
finally {
sDb.commit();
}
}
/* UPDATE tableName SET checksum=(SELECT checksum FROM pristine WHERE md5_checksum=tableName.checksum)
WHERE EXISTS(SELECT 1 FROM pristine WHERE md5_checksum=WORKING_NODE.checksum); */
private static class UpdateChecksum {
private SVNSqlJetDb sDb;
private Enum<?> tableName;
public UpdateChecksum(SVNSqlJetDb sDb, Enum<?> tableName) {
this.sDb = sDb;
this.tableName = tableName;
}
public void run() throws SVNException {
SVNSqlJetUpdateStatement stmt = new SVNSqlJetUpdateStatement(sDb, tableName) {
private SVNSqlJetSelectFieldsStatement<SVNWCDbSchema.PRISTINE__Fields> select =
new SVNSqlJetSelectFieldsStatement<SVNWCDbSchema.PRISTINE__Fields>(sDb, SVNWCDbSchema.PRISTINE) {
protected boolean isFilterPassed() throws SVNException {
return ((String)getBind(1)).equals(getColumnString(SVNWCDbSchema.PRISTINE__Fields.md5_checksum));
}
protected void defineFields() {
fields.add(SVNWCDbSchema.PRISTINE__Fields.checksum);
}
protected Object[] getWhere() throws SVNException {
return new Object[] {};
}
};
public Map<String, Object> getUpdateValues() throws SVNException {
return null;
}
public long exec() throws SVNException {
long n = 0;
try {
statementStarted();
while (next()) {
Map<String, Object> rowValues = getRowValues();
String checksum = (String)rowValues.get("checksum");
if (checksum == null)
continue;
select.bindString(1, checksum);
try {
if (select.next()) {
rowValues.put("checksum", select.getColumnString(SVNWCDbSchema.PRISTINE__Fields.checksum));
} else {
continue;
}
} finally {
select.reset();
}
update(rowValues);
n++;
}
statementCompleted(null);
} catch (SqlJetException e) {
statementCompleted(e);
SVNSqlJetDb.createSqlJetError(e);
}
return n;
}
};
try {
stmt.exec();
} finally {
stmt.reset();
}
}
}
private static class bumpTo20 implements Bumpable {
public void bumpTo(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException {
try {
sDb.getDb().createTable("CREATE TABLE NODES ( wc_id INTEGER NOT NULL REFERENCES WCROOT (id), local_relpath TEXT NOT NULL, op_depth INTEGER NOT NULL, "
+ " parent_relpath TEXT, repos_id INTEGER REFERENCES REPOSITORY (id), repos_path TEXT, revision INTEGER, presence TEXT NOT NULL, "
+ " moved_here INTEGER, moved_to TEXT, kind TEXT NOT NULL, properties BLOB, depth TEXT, checksum TEXT REFERENCES PRISTINE (checksum), symlink_target TEXT, "
+ " changed_revision INTEGER, changed_date INTEGER, changed_author TEXT, translated_size INTEGER, last_mod_time INTEGER, "
+ " dav_cache BLOB, file_external TEXT, PRIMARY KEY (wc_id, local_relpath, op_depth) ); ");
sDb.getDb().createIndex("CREATE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath, op_depth); ");
/*
UPDATE BASE_NODE SET checksum=(SELECT checksum FROM pristine WHERE md5_checksum=BASE_NODE.checksum)
WHERE EXISTS(SELECT 1 FROM pristine WHERE md5_checksum=BASE_NODE.checksum);
*/
UpdateChecksum uc = new UpdateChecksum(sDb, SVNWCDbSchema.BASE_NODE);
uc.run();
/*
UPDATE WORKING_NODE SET checksum=(SELECT checksum FROM pristine WHERE md5_checksum=WORKING_NODE.checksum)
WHERE EXISTS(SELECT 1 FROM pristine WHERE md5_checksum=WORKING_NODE.checksum);
*/
uc = new UpdateChecksum(sDb, SVNWCDbSchema.WORKING_NODE);
uc.run();
/*
INSERT INTO NODES (
wc_id, local_relpath, op_depth, parent_relpath,
repos_id, repos_path, revision,
presence, depth, moved_here, moved_to, kind,
changed_revision, changed_date, changed_author,
checksum, properties, translated_size, last_mod_time,
dav_cache, symlink_target, file_external )
SELECT wc_id, local_relpath, 0 /*op_depth/, parent_relpath,
repos_id, repos_relpath, revnum,
presence, depth, NULL /*moved_here/, NULL /*moved_to/, kind,
changed_rev, changed_date, changed_author,
checksum, properties, translated_size, last_mod_time,
dav_cache, symlink_target, file_external
FROM BASE_NODE;
*/
SVNSqlJetInsertStatement stmt = new SVNSqlJetInsertStatement(sDb, SVNWCDbSchema.NODES) {
private SVNSqlJetSelectStatement select = new SVNSqlJetSelectStatement(sDb.getTemporaryDb(), SVNWCDbSchema.BASE_NODE) {};
public long exec() throws SVNException {
try {
int n = 0;
while (select.next()) {
try {
table.insertByFieldNamesOr(null, getInsertValues());
n++;
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
return -1;
}
}
return n;
} finally {
select.reset();
}
}
protected Map<String, Object> getInsertValues() throws SVNException {
Map<String,Object> selectedRow = select.getRowValues();
Map<String, Object> insertValues = new HashMap<String, Object>();
insertValues.put(SVNWCDbSchema.NODES__Fields.wc_id.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.wc_id.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.local_relpath.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.local_relpath.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.op_depth.toString(), 0);
insertValues.put(SVNWCDbSchema.NODES__Fields.parent_relpath.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.parent_relpath.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.repos_id.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.repos_id.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.repos_path.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.repos_path.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.revision.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.revnum.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.presence.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.presence.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.depth.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.depth.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.moved_here.toString(), null);
insertValues.put(SVNWCDbSchema.NODES__Fields.moved_to.toString(), null);
insertValues.put(SVNWCDbSchema.NODES__Fields.kind.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.kind.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.changed_revision.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.changed_rev.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.changed_date.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.changed_date.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.changed_author.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.changed_author.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.checksum.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.checksum.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.properties.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.properties.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.translated_size.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.translated_size.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.last_mod_time.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.last_mod_time.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.dav_cache.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.dav_cache.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.symlink_target.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.symlink_target.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.file_external.toString(), selectedRow.get(SVNWCDbSchema.BASE_NODE__Fields.file_external.toString()));
return insertValues;
}
};
try {
stmt.exec();
} finally {
stmt.reset();
}
/*INSERT INTO NODES (
wc_id, local_relpath, op_depth, parent_relpath,
repos_id, repos_path, revision,
presence, depth, moved_here, moved_to, kind,
changed_revision, changed_date, changed_author,
checksum, properties, translated_size, last_mod_time,
dav_cache, symlink_target, file_external )
SELECT wc_id, local_relpath, 2 /*op_depth/, parent_relpath,
copyfrom_repos_id, copyfrom_repos_path, copyfrom_revnum,
presence, depth, NULL /*moved_here/, NULL /*moved_to/, kind,
changed_rev, changed_date, changed_author,
checksum, properties, translated_size, last_mod_time,
NULL /*dav_cache/, symlink_target, NULL /*file_external/
FROM WORKING_NODE;
*/
stmt = new SVNSqlJetInsertStatement(sDb, SVNWCDbSchema.NODES) {
private SVNSqlJetSelectStatement select = new SVNSqlJetSelectStatement(sDb.getTemporaryDb(), SVNWCDbSchema.BASE_NODE) {};
public long exec() throws SVNException {
try {
int n = 0;
while (select.next()) {
try {
table.insertByFieldNamesOr(null, getInsertValues());
n++;
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
return -1;
}
}
return n;
} finally {
select.reset();
}
}
protected Map<String, Object> getInsertValues() throws SVNException {
Map<String,Object> selectedRow = select.getRowValues();
Map<String, Object> insertValues = new HashMap<String, Object>();
insertValues.put(SVNWCDbSchema.NODES__Fields.wc_id.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.wc_id.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.local_relpath.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.local_relpath.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.op_depth.toString(), 2);
insertValues.put(SVNWCDbSchema.NODES__Fields.parent_relpath.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.parent_relpath.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.repos_id.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.copyfrom_repos_id.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.repos_path.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.copyfrom_repos_path.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.revision.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.copyfrom_revnum.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.presence.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.presence.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.depth.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.depth.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.moved_here.toString(), null);
insertValues.put(SVNWCDbSchema.NODES__Fields.moved_to.toString(), null);
insertValues.put(SVNWCDbSchema.NODES__Fields.kind.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.kind.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.changed_revision.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.changed_rev.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.changed_date.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.changed_date.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.changed_author.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.changed_author.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.checksum.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.checksum.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.properties.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.properties.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.translated_size.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.translated_size.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.last_mod_time.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.last_mod_time.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.dav_cache.toString(), null);
insertValues.put(SVNWCDbSchema.NODES__Fields.symlink_target.toString(), selectedRow.get(SVNWCDbSchema.WORKING_NODE__Fields.symlink_target.toString()));
insertValues.put(SVNWCDbSchema.NODES__Fields.file_external.toString(), null);
return insertValues;
}
};
try {
stmt.exec();
} finally {
stmt.reset();
}
sDb.getDb().dropTable("BASE_NODE");
sDb.getDb().dropTable("WORKING_NODE");
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
}
setVersion(sDb, (int)21);
}
}
private static class bumpTo21 implements Bumpable {
public void bumpTo(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException {
setVersion(sDb, (int)21);
migrateTreeConflictData(sDb);
}
}
private static class bumpTo22 implements Bumpable {
public void bumpTo(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException {
/*-- STMT_UPGRADE_TO_22
UPDATE actual_node SET tree_conflict_data = conflict_data;
UPDATE actual_node SET conflict_data = NULL;
*/
SVNSqlJetUpdateStatement stmt = new SVNSqlJetUpdateStatement(sDb, SVNWCDbSchema.ACTUAL_NODE) {
public Map<String, Object> getUpdateValues() throws SVNException {
Map<String, Object> rowValues = getRowValues();
rowValues.put(SVNWCDbSchema.ACTUAL_NODE__Fields.tree_conflict_data.toString(),
rowValues.get(SVNWCDbSchema.ACTUAL_NODE__Fields.conflict_data.toString()));
return rowValues;
}
};
try {
stmt.exec();
} finally {
stmt.reset();
}
stmt = new SVNSqlJetUpdateStatement(sDb, SVNWCDbSchema.ACTUAL_NODE) {
public Map<String, Object> getUpdateValues() throws SVNException {
Map<String, Object> rowValues = getRowValues();
rowValues.put(SVNWCDbSchema.ACTUAL_NODE__Fields.conflict_data.toString(), null);
return rowValues;
}
};
try {
stmt.exec();
} finally {
stmt.reset();
}
setVersion(sDb, (int)22);
}
}
private static class bumpTo23 implements Bumpable {
public void bumpTo(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException {
//-- STMT_HAS_WORKING_NODES
//SELECT 1 FROM nodes WHERE op_depth > 0
//LIMIT 1
SVNSqlJetSelectFieldsStatement<SVNWCDbSchema.NODES__Fields> stmt = new SVNSqlJetSelectFieldsStatement<SVNWCDbSchema.NODES__Fields>(sDb, SVNWCDbSchema.NODES) {
protected void defineFields() {
fields.add(SVNWCDbSchema.NODES__Fields.wc_id);
}
protected boolean isFilterPassed() throws SVNException {
return getColumnLong(SVNWCDbSchema.NODES__Fields.op_depth) > 0;
}
};
boolean haveRow = false;
try {
haveRow = stmt.next();
} finally {
stmt.reset();
}
if (haveRow) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE,
"The working copy at ''{0}'' is format 22 with WORKING nodes; use a format 22 client to diff/revert before using this client",
wcRootAbsPath);
SVNErrorManager.error(err, SVNLogType.WC);
}
setVersion(sDb, (int)23);
}
}
private static class bumpTo24 implements Bumpable {
public void bumpTo(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException {
/*-- STMT_UPGRADE_TO_25
UPDATE pristine SET refcount = (SELECT COUNT(*) FROM nodes WHERE checksum = pristine.checksum /*OR checksum = pristine.md5_checksum/);
*/
SVNSqlJetUpdateStatement stmt = new SVNSqlJetUpdateStatement(sDb, SVNWCDbSchema.PRISTINE) {
private SVNSqlJetSelectFieldsStatement<SVNWCDbSchema.NODES__Fields> select =
new SVNSqlJetSelectFieldsStatement<SVNWCDbSchema.NODES__Fields>(sDb, SVNWCDbSchema.NODES) {
protected boolean isFilterPassed() throws SVNException {
return ((String)getBind(1)).equals(getColumnString(SVNWCDbSchema.NODES__Fields.checksum));
}
protected void defineFields() {
fields.add(SVNWCDbSchema.NODES__Fields.wc_id);
}
protected Object[] getWhere() throws SVNException {
return new Object[] {};
}
};
public Map<String, Object> getUpdateValues() throws SVNException {
Map<String, Object> rowValues = getRowValues();
select.bindString(1, (String)rowValues.get(SVNWCDbSchema.PRISTINE__Fields.checksum.toString()));
long rowCount = 0;
try {
while (select.next())
rowCount++;
} finally {
select.reset();
}
rowValues.put(SVNWCDbSchema.PRISTINE__Fields.refcount.toString(), rowCount);
return rowValues;
}
};
try {
stmt.exec();
} finally {
stmt.reset();
}
setVersion(sDb, (int)24);
try {
sDb.getDb().createTrigger("CREATE TRIGGER nodes_insert_trigger AFTER INSERT ON nodes WHEN NEW.checksum IS NOT NULL BEGIN UPDATE pristine SET refcount = refcount + 1 WHERE checksum = NEW.checksum; END;");
sDb.getDb().createTrigger("CREATE TRIGGER nodes_delete_trigger AFTER DELETE ON nodes WHEN OLD.checksum IS NOT NULL BEGIN UPDATE pristine SET refcount = refcount - 1 WHERE checksum = OLD.checksum; END;");
sDb.getDb().createTrigger("CREATE TRIGGER nodes_update_checksum_trigger AFTER UPDATE OF checksum ON nodes WHEN NEW.checksum IS NOT OLD.checksum BEGIN UPDATE pristine SET refcount = refcount + 1 WHERE checksum = NEW.checksum; UPDATE pristine SET refcount = refcount - 1 WHERE checksum = OLD.checksum; END;");
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
}
}
}
private static class bumpTo25 implements Bumpable {
public void bumpTo(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException {
/*-- STMT_UPGRADE_TO_25
//DROP VIEW IF EXISTS NODES_CURRENT;
CREATE VIEW NODES_CURRENT AS
SELECT * FROM nodes JOIN (SELECT wc_id, local_relpath, MAX(op_depth) AS op_depth FROM nodes GROUP BY wc_id, local_relpath) AS filter
ON nodes.wc_id = filter.wc_id AND nodes.local_relpath = filter.local_relpath AND nodes.op_depth = filter.op_depth;
*/
try {
sDb.getDb().createView(
"CREATE VIEW NODES_CURRENT AS " +
"SELECT * FROM nodes JOIN (SELECT wc_id, local_relpath, MAX(op_depth) AS op_depth FROM nodes GROUP BY wc_id, local_relpath) AS filter " +
"ON nodes.wc_id = filter.wc_id AND nodes.local_relpath = filter.local_relpath AND nodes.op_depth = filter.op_depth;");
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
}
setVersion(sDb, (int)25);
}
}
private static class bumpTo26 implements Bumpable {
public void bumpTo(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException {
/*-- STMT_UPGRADE_TO_26
* DROP VIEW IF EXISTS NODES_BASE;
* CREATE VIEW NODES_BASE AS
* SELECT * FROM nodes
* WHERE op_depth = 0;
*/
try {
if (sDb.getDb().getSchema().getViewNames().contains("NODES_BASE")) {
sDb.getDb().dropView("NODES_BASE");
}
sDb.getDb().createView("CREATE VIEW NODES_BASE AS SELECT * FROM nodes WHERE op_depth = 0;");
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
}
setVersion(sDb, (int)26);
}
}
private static class bumpTo27 implements Bumpable {
public void bumpTo(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException {
/*
-- STMT_HAS_ACTUAL_NODES_CONFLICTS
SELECT 1 FROM actual_node
WHERE NOT ((prop_reject IS NULL) AND (conflict_old IS NULL)
AND (conflict_new IS NULL) AND (conflict_working IS NULL)
AND (tree_conflict_data IS NULL))
LIMIT 1
*/
SVNSqlJetSelectFieldsStatement<SVNWCDbSchema.ACTUAL_NODE__Fields> stmt = new SVNSqlJetSelectFieldsStatement<SVNWCDbSchema.ACTUAL_NODE__Fields>(sDb, SVNWCDbSchema.ACTUAL_NODE) {
protected void defineFields() {
fields.add(SVNWCDbSchema.ACTUAL_NODE__Fields.wc_id);
}
protected boolean isFilterPassed() throws SVNException {
return !(isColumnNull(SVNWCDbSchema.ACTUAL_NODE__Fields.prop_reject) &&
isColumnNull(SVNWCDbSchema.ACTUAL_NODE__Fields.conflict_old) &&
isColumnNull(SVNWCDbSchema.ACTUAL_NODE__Fields.conflict_new) &&
isColumnNull(SVNWCDbSchema.ACTUAL_NODE__Fields.conflict_working) &&
isColumnNull(SVNWCDbSchema.ACTUAL_NODE__Fields.tree_conflict_data));
}
};
boolean haveRow = false;
try {
haveRow = stmt.next();
} finally {
stmt.reset();
}
if (haveRow) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE,
"The working copy at ''{0}'' is format 26 with conflicts; use a format 26 client to resolve before using this client",
wcRootAbsPath);
SVNErrorManager.error(err, SVNLogType.WC);
}
setVersion(sDb, (int)27);
}
}
private static class bumpTo28 implements Bumpable {
public void bumpTo(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException {
/*-- STMT_UPGRADE_TO_28
* UPDATE NODES SET checksum=(SELECT checksum FROM pristine WHERE md5_checksum=nodes.checksum)
* WHERE EXISTS(SELECT 1 FROM pristine WHERE md5_checksum=nodes.checksum);
*/
UpdateChecksum uc = new UpdateChecksum(sDb, SVNWCDbSchema.NODES);
uc.run();
setVersion(sDb, 28);
}
}
private static class bumpTo29 implements Bumpable {
public void bumpTo(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException {
/* Rename all pristine files, adding a ".svn-base" suffix. */
File pristineDirAbsPath = SVNWCUtils.admChild(wcRootAbsPath, ISVNWCDb.PRISTINE_STORAGE_RELPATH);
for (File dir: SVNFileListUtil.listFiles(pristineDirAbsPath)) {
for (File file: SVNFileListUtil.listFiles(dir)) {
/* If FINFO indicates that ABSPATH names a file, rename it to '<ABSPATH>.svn-base'.
*
* Ignore any file whose name is not the expected length, in order to make life easier for any developer
* who runs this code twice or has some non-standard files in the pristine directory.
*/
if (SVNFileType.getType(file) == SVNFileType.FILE && SVNFileUtil.getFileName(file).length() == PRISTINE_BASENAME_OLD_LEN) {
File newAbsPath = SVNFileUtil.createFilePath(SVNFileUtil.getFilePath(file) + PRISTINE_STORAGE_EXT);
SVNFileUtil.rename(file, newAbsPath);
}
}
}
/* Externals */
try {
sDb.getDb().createTable("CREATE TABLE EXTERNALS ( " +
" wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " +
" local_relpath TEXT NOT NULL, " +
" parent_relpath TEXT NOT NULL, " +
" repos_id INTEGER NOT NULL REFERENCES REPOSITORY (id), " +
" presence TEXT NOT NULL, " +
" kind TEXT NOT NULL, " +
" def_local_relpath TEXT NOT NULL, " +
" def_repos_relpath TEXT NOT NULL, " +
" def_operational_revision TEXT, " +
" def_revision TEXT, " +
" PRIMARY KEY (wc_id, local_relpath) " +
"); ");
sDb.getDb().createIndex("CREATE INDEX I_EXTERNALS_PARENT ON EXTERNALS (wc_id, parent_relpath);");
sDb.getDb().createIndex("CREATE UNIQUE INDEX I_EXTERNALS_DEFINED ON EXTERNALS (wc_id, def_local_relpath, local_relpath);");
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
}
upgradeExternals(sDb, wcRootAbsPath);
/* Format 29 introduces the EXTERNALS table (See STMT_CREATE_TRIGGERS) and optimizes a few trigger definitions. ... */
//-- STMT_UPGRADE_TO_29
try {
if (sDb.getDb().getSchema().getTriggerNames().contains("nodes_update_checksum_trigger")) {
sDb.getDb().dropTrigger("nodes_update_checksum_trigger");
}
if (sDb.getDb().getSchema().getTriggerNames().contains("nodes_insert_trigger")) {
sDb.getDb().dropTrigger("nodes_insert_trigger");
}
if (sDb.getDb().getSchema().getTriggerNames().contains("nodes_delete_trigger")) {
sDb.getDb().dropTrigger("nodes_delete_trigger");
}
sDb.getDb().createTrigger("CREATE TRIGGER nodes_update_checksum_trigger AFTER UPDATE OF checksum ON nodes WHEN NEW.checksum IS NOT OLD.checksum BEGIN UPDATE pristine SET refcount = refcount + 1 WHERE checksum = NEW.checksum; UPDATE pristine SET refcount = refcount - 1 WHERE checksum = OLD.checksum; END;");
sDb.getDb().createTrigger("CREATE TRIGGER nodes_insert_trigger AFTER INSERT ON nodes WHEN NEW.checksum IS NOT NULL BEGIN UPDATE pristine SET refcount = refcount + 1 WHERE checksum = NEW.checksum; END;");
sDb.getDb().createTrigger("CREATE TRIGGER nodes_delete_trigger AFTER DELETE ON nodes WHEN OLD.checksum IS NOT NULL BEGIN UPDATE pristine SET refcount = refcount - 1 WHERE checksum = OLD.checksum; END;");
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
}
setVersion(sDb, 29);
}
}
private static class bumpTo30 implements Bumpable {
public void bumpTo(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException {
try {
if (sDb.getDb().getSchema().getTable("NODES").getColumn("inherited_props") == null) {
sDb.getDb().createIndex("CREATE UNIQUE INDEX IF NOT EXISTS I_NODES_MOVED ON NODES (wc_id, moved_to, op_depth);");
sDb.getDb().createIndex("CREATE INDEX IF NOT EXISTS I_PRISTINE_MD5 ON PRISTINE (md5_checksum);");
final ISqlJetCursor actulaNode = sDb.getDb().getTable("ACTUAL_NODE").open();
while(!actulaNode.eof()) {
final String conflictOld = actulaNode.getString(ACTUAL_NODE__Fields.conflict_old.toString());
final String conflictWorking = actulaNode.getString(ACTUAL_NODE__Fields.conflict_working.toString());
final String conflictNew = actulaNode.getString(ACTUAL_NODE__Fields.conflict_new.toString());
final String propReject = actulaNode.getString(ACTUAL_NODE__Fields.prop_reject.toString());
final byte[] treeConflictData = actulaNode.getBlobAsArray(ACTUAL_NODE__Fields.tree_conflict_data.toString());
if (conflictOld == null && conflictWorking == null && conflictNew == null && propReject == null && treeConflictData == null) {
actulaNode.next();
continue;
}
final String localRelpath = actulaNode.getString(ACTUAL_NODE__Fields.local_relpath.toString());
final SVNSkel conflictData = SvnWcDbConflicts.convertToConflictSkel(wcRootAbsPath, db, localRelpath, conflictOld, conflictWorking, conflictNew, propReject, treeConflictData);
final Map<String, Object> newRowValues = new HashMap<String, Object>();
if (conflictData != null) {
newRowValues.put(ACTUAL_NODE__Fields.conflict_data.toString(), conflictData.unparse());
}
newRowValues.put(ACTUAL_NODE__Fields.conflict_old.toString(), null);
newRowValues.put(ACTUAL_NODE__Fields.conflict_working.toString(), null);
newRowValues.put(ACTUAL_NODE__Fields.conflict_new.toString(), null);
newRowValues.put(ACTUAL_NODE__Fields.prop_reject.toString(), null);
newRowValues.put(ACTUAL_NODE__Fields.tree_conflict_data.toString(), null);
actulaNode.updateByFieldNames(newRowValues);
actulaNode.next();
}
}
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
}
setVersion(sDb, 30);
}
}
private static class bumpTo31 implements Bumpable {
public void bumpTo(SVNWCDb db, SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException {
try {
if (sDb.getDb().getSchema().getTable("NODES").getColumn("inherited_props") != null) {
setVersion(sDb, 31);
return;
} else {
if (sDb.getDb().getSchema().getIndex("I_ACTUAL_CHANGELIST") != null) {
sDb.getDb().dropIndex("I_ACTUAL_CHANGELIST");
}
if (sDb.getDb().getSchema().getIndex("I_EXTERNALS_PARENT") != null) {
sDb.getDb().dropIndex("I_EXTERNALS_PARENT");
}
sDb.getDb().alterTable("ALTER TABLE NODES ADD COLUMN inherited_props BLOB;");
sDb.getDb().dropIndex("I_NODES_PARENT");
sDb.getDb().createIndex("CREATE UNIQUE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath, local_relpath, op_depth);");
sDb.getDb().dropIndex("I_ACTUAL_PARENT");
sDb.getDb().createIndex("CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath, local_relpath);");
}
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
}
final SVNSqlJetStatement stmt = sDb.getStatement(SVNWCDbStatements.SELECT_WCROOT_NODES);
final SVNSqlJetStatement markRootStmt = sDb.getStatement(SVNWCDbStatements.UPDATE_IPROPS);
final byte[] emptyInheritedProps = SVNSkel.createEmptyList().unparse();
try {
while(stmt.next()) {
final long wcId = stmt.getColumnLong(NODES__Fields.wc_id);
final String localRelPath = stmt.getColumnString(NODES__Fields.local_relpath);
try {
markRootStmt.bindf("isb", wcId, localRelPath, emptyInheritedProps);
markRootStmt.done();
} finally {
markRootStmt.reset();
}
}
} finally {
stmt.reset();
}
setVersion(sDb, 31);
}
}
private static void upgradeExternals(SVNSqlJetDb sDb, File wcRootAbsPath) throws SVNException {
SVNSqlJetStatement stmt = sDb.getStatement(SVNWCDbStatements.SELECT_EXTERNAL_PROPERTIES);
/* ### For this intermediate upgrade we just assume WC_ID = 1.
### Before this bump we lost track of externals all the time, so lets keep this easy. */
stmt.bindf("is", 1, "");
try {
while (stmt.next()) {
SVNProperties props = stmt.getColumnProperties(SVNWCDbSchema.NODES__Fields.properties);
String externalsValues = props.getStringValue(SVNProperty.EXTERNALS);
File localRelPath = SVNFileUtil.createFilePath(stmt.getColumnString(SVNWCDbSchema.NODES__Fields.properties));
File localAbsPath = SVNFileUtil.createFilePath(wcRootAbsPath, localRelPath);
if (externalsValues != null) {
SVNExternal[] externalDefs = SVNExternal.parseExternals(localAbsPath, externalsValues);
for (SVNExternal externalDef : externalDefs) {
File externalPath = SVNFileUtil.createFilePath(localAbsPath, externalDef.getPath());
/* Insert dummy externals definitions: Insert an unknown external, to make sure it will be cleaned up when it is not
updated on the next update. */
SVNSqlJetStatement addStmt = sDb.getStatement(SVNWCDbStatements.INSERT_EXTERNAL);
try {
addStmt.bindf("isssssis",
1, /* wc_id*/
SVNFileUtil.getFilePath(externalPath),
SVNFileUtil.getFilePath(SVNFileUtil.getFileDir(externalPath)),
"normal",
"unknown",
SVNFileUtil.getFilePath(localRelPath),
1, /* repos_id */
"" /* repos_relpath */);
addStmt.exec();
} finally {
addStmt.reset();
}
}
}
}
} finally {
stmt.reset();
}
}
}