Closes #711, Remove support for nested transactions

This commit is contained in:
Dan Stillman 2015-05-10 04:20:47 -04:00
parent e584dbf5dd
commit 14d435b8d8
23 changed files with 862 additions and 856 deletions

View file

@ -1109,7 +1109,7 @@
this.item.setType(itemTypeID);
if (this.saveOnEdit) {
this.item.save();
this.item.saveTx();
}
else {
this.refresh();
@ -1357,7 +1357,7 @@
return;
}
this.item.removeCreator(index);
this.item.save();
this.item.saveTx();
]]>
</body>
</method>
@ -1766,7 +1766,7 @@
}
if (this.saveOnEdit) {
yield this.item.save();
yield this.item.saveTx();
}
var val = this.item.getCreator(creatorIndex);
@ -1915,7 +1915,7 @@
return Zotero.spawn(function* () {
this.item.setField(field, value);
if (save) {
yield this.item.save();
yield this.item.saveTx();
}
}, this);
]]></body>
@ -2034,14 +2034,14 @@
}
this.item.removeCreator(index);
if (this.saveOnEdit && !skipSave) {
return this.item.save();
return this.item.saveTx();
}
return;
}
var changed = this.item.setCreator(index, fields);
if (changed && this.saveOnEdit && !skipSave) {
return this.item.save();
return this.item.saveTx();
}
}.bind(this));
]]></body>
@ -2091,7 +2091,7 @@
this.item.setCreator(newIndex, a);
this.item.setCreator(index, b);
if (this.saveOnEdit) {
return this.item.save();
return this.item.saveTx();
}
}.bind(this));
]]>

View file

@ -239,7 +239,7 @@
let changed = this.item.setNote(noteField.value);
if (changed && this.saveOnEdit) {
yield this.item.save();
yield this.item.saveTx();
}
return;
}
@ -254,7 +254,7 @@
item.parentKey = this.parentItem.key;
}
if (this.saveOnEdit) {
var id = yield item.save();
var id = yield item.saveTx();
if (!this.parentItem && this.collection) {
this.collection.addItem(id);

View file

@ -333,7 +333,7 @@
if (tagData) {
let item = document.getBindingParent(this).item
item.removeTag(tagName);
yield item.save()
yield item.saveTx()
}
// Return focus to items pane
@ -682,13 +682,13 @@
if (oldValue !== value) {
// The existing textbox will be removed in notify()
this.item.replaceTag(oldValue, value);
yield this.item.save();
yield this.item.saveTx();
}
}
// Existing tag cleared
else {
this.item.removeTag(oldValue);
yield this.item.save();
yield this.item.saveTx();
}
}
// Multiple tags
@ -710,7 +710,7 @@
}
this.item.addTags(tags);
yield this.item.save();
yield this.item.saveTx();
if (lastTag) {
this._lastTabIndex = this.item.getTags().length;
@ -724,7 +724,7 @@
// if it doesn't already exist.
row.parentNode.removeChild(row);
this.item.addTag(value);
yield this.item.save();
yield this.item.saveTx();
}
}.bind(this));
]]></body>

View file

@ -45,7 +45,7 @@ Zotero.Attachments = new function(){
throw ("'" + file.leafName + "' must be a file in Zotero.Attachments.importFromFile()");
}
var itemID, newFile;
var itemID, newFile, contentType;
yield Zotero.DB.executeTransaction(function* () {
// Create a new attachment
var attachmentItem = new Zotero.Item('attachment');
@ -73,15 +73,15 @@ Zotero.Attachments = new function(){
// Copy file to unique filename, which automatically shortens long filenames
newFile = Zotero.File.copyToUnique(file, newFile);
var contentType = yield Zotero.MIME.getMIMETypeFromFile(newFile);
contentType = yield Zotero.MIME.getMIMETypeFromFile(newFile);
attachmentItem.attachmentContentType = contentType;
attachmentItem.attachmentPath = this.getPath(newFile, this.LINK_MODE_IMPORTED_FILE);
yield attachmentItem.save();
// Determine charset and build fulltext index
yield _postProcessFile(itemID, newFile, contentType);
}.bind(this))
.then(function () {
return _postProcessFile(itemID, newFile, contentType);
})
.catch(function (e) {
Zotero.debug(e, 1);
var msg = "Failed importing file " + file.path;
@ -112,21 +112,21 @@ Zotero.Attachments = new function(){
var title = file.leafName;
var contentType = yield Zotero.MIME.getMIMETypeFromFile(file);
var itemID;
return Zotero.DB.executeTransaction(function* () {
var itemID = yield _addToDB({
itemID = yield _addToDB({
file: file,
title: title,
linkMode: this.LINK_MODE_LINKED_FILE,
contentType: contentType,
parentItemID: parentItemID
});
// Determine charset and build fulltext index
yield _postProcessFile(itemID, file, contentType);
return itemID;
}.bind(this));
}.bind(this))
.then(function () {
return _postProcessFile(itemID, file, contentType);
})
.then(() => itemID);
});
@ -148,7 +148,7 @@ Zotero.Attachments = new function(){
throw new Error("parentItemID not provided");
}
var destDir;
var itemID, destDir, newFile;
yield Zotero.DB.executeTransaction(function* () {
// Create a new attachment
var attachmentItem = new Zotero.Item('attachment');
@ -164,7 +164,7 @@ Zotero.Attachments = new function(){
// DEBUG: this should probably insert access date too so as to
// create a proper item, but at the moment this is only called by
// translate.js, which sets the metadata fields itself
var itemID = yield attachmentItem.save();
itemID = yield attachmentItem.save();
attachmentItem = yield Zotero.Items.getAsync(itemID)
destDir = this.getStorageDirectory(attachmentItem);
@ -172,15 +172,15 @@ Zotero.Attachments = new function(){
file.parent.copyTo(storageDir, destDir.leafName);
// Point to copied file
var newFile = destDir.clone();
newFile = destDir.clone();
newFile.append(file.leafName);
attachmentItem.attachmentPath = this.getPath(newFile, this.LINK_MODE_IMPORTED_URL);
yield attachmentItem.save();
// Determine charset and build fulltext index
yield _postProcessFile(itemID, newFile, contentType);
}.bind(this))
.then(function () {
return _postProcessFile(itemID, newFile, contentType);
})
.catch(function (e) {
Zotero.debug(e, 1);
@ -1130,7 +1130,7 @@ Zotero.Attachments = new function(){
if (parentItemID) {
newAttachment.parentID = parentItemID;
}
yield newAttachment.save();
yield newAttachment.saveTx();
// Copy over files if they exist
if (newAttachment.isImportedAttachment() && attachment.getFile()) {
@ -1362,7 +1362,7 @@ Zotero.Attachments = new function(){
var item = yield Zotero.Items.getAsync(itemID);
charset = yield Zotero.CharacterSets.add(charset);
item.attachmentCharset = charset;
yield item.save();
yield item.saveTx();
if (disabled) {
Zotero.Notifier.enable();

View file

@ -70,31 +70,31 @@ Zotero.Creators = new function() {
/**
* Returns the creatorID matching given fields, or creates a new creator and returns its id
*
* @requireTransaction
* @param {Object} data Creator data in API JSON format
* @param {Boolean} [create=false] If no matching creator, create one
* @return {Promise<Integer>} creatorID
*/
this.getIDFromData = Zotero.Promise.method(function (data, create) {
this.getIDFromData = Zotero.Promise.coroutine(function* (data, create) {
Zotero.DB.requireTransaction();
data = this.cleanData(data);
return Zotero.DB.executeTransaction(function* () {
var sql = "SELECT creatorID FROM creators WHERE "
+ "firstName=? AND lastName=? AND fieldMode=?";
var id = yield Zotero.DB.valueQueryAsync(
sql, [data.firstName, data.lastName, data.fieldMode]
var sql = "SELECT creatorID FROM creators WHERE "
+ "firstName=? AND lastName=? AND fieldMode=?";
var id = yield Zotero.DB.valueQueryAsync(
sql, [data.firstName, data.lastName, data.fieldMode]
);
if (!id && create) {
id = yield Zotero.ID.get('creators');
let sql = "INSERT INTO creators (creatorID, firstName, lastName, fieldMode) "
+ "VALUES (?, ?, ?, ?)";
let insertID = yield Zotero.DB.queryAsync(
sql, [id, data.firstName, data.lastName, data.fieldMode]
);
if (!id && create) {
id = yield Zotero.ID.get('creators');
let sql = "INSERT INTO creators (creatorID, firstName, lastName, fieldMode) "
+ "VALUES (?, ?, ?, ?)";
let insertID = yield Zotero.DB.queryAsync(
sql, [id, data.firstName, data.lastName, data.fieldMode]
);
if (!id) {
id = insertID;
}
if (!id) {
id = insertID;
}
return id;
});
}
return id;
});

View file

@ -547,11 +547,19 @@ Zotero.DataObject.prototype.editCheck = function () {
* TRUE on item update, or FALSE if item was unchanged
*/
Zotero.DataObject.prototype.save = Zotero.Promise.coroutine(function* (options) {
options = options || {};
var env = {
options: options || {},
options: options,
transactionOptions: {}
};
if (!env.options.tx && !Zotero.DB.inTransaction()) {
Zotero.logError("save() called on Zotero." + this._ObjectType + " without a wrapping "
+ "transaction -- use saveTx() instead");
Zotero.debug((new Error).stack, 2);
env.options.tx = true;
}
var proceed = yield this._initSave(env);
if (!proceed) return false;
@ -562,7 +570,7 @@ Zotero.DataObject.prototype.save = Zotero.Promise.coroutine(function* (options)
Zotero.debug('Updating database with new ' + this._objectType + ' data', 4);
}
return Zotero.DB.executeTransaction(function* () {
try {
if (Zotero.DataObject.prototype._finalizeSave == this._finalizeSave) {
throw new Error("_finalizeSave not implement for Zotero." + this._ObjectType);
}
@ -574,27 +582,49 @@ Zotero.DataObject.prototype.save = Zotero.Promise.coroutine(function* (options)
if (!env.isNew) {
env.changed = this._previousData;
}
yield this._saveData(env);
yield Zotero.DataObject.prototype._finalizeSave.call(this, env);
return this._finalizeSave(env);
}.bind(this), env.transactionOptions)
.catch(e => {
// Create transaction
if (env.options.tx) {
let result = yield Zotero.DB.executeTransaction(function* () {
yield this._saveData(env);
yield Zotero.DataObject.prototype._finalizeSave.call(this, env);
return this._finalizeSave(env);
}.bind(this), env.transactionOptions);
return result;
}
// Use existing transaction
else {
Zotero.DB.requireTransaction();
yield this._saveData(env);
yield Zotero.DataObject.prototype._finalizeSave.call(this, env);
return this._finalizeSave(env);
}
}
catch(e) {
return this._recoverFromSaveError(env, e)
.catch(function(e2) {
Zotero.debug(e2, 1);
})
.then(function() {
if (options.errorHandler) {
options.errorHandler(e);
if (env.options.errorHandler) {
env.options.errorHandler(e);
}
else {
Zotero.debug(e, 1);
}
throw e;
})
});
}
});
Zotero.DataObject.prototype.saveTx = function (options) {
options = options || {};
options.tx = true;
return this.save(options);
}
Zotero.DataObject.prototype.hasChanged = function() {
Zotero.debug(this._changed);
return !!Object.keys(this._changed).filter(dataType => this._changed[dataType]).length
@ -618,9 +648,15 @@ Zotero.DataObject.prototype._initSave = Zotero.Promise.coroutine(function* (env)
}
// Undo registerIdentifiers() on failure
env.transactionOptions.onRollback = function () {
var func = function () {
this.ObjectsClass.unload(env.id);
}.bind(this);
if (env.options.tx) {
env.transactionOptions.onRollback = func;
}
else {
Zotero.DB.addCurrentCallback("rollback", func);
}
return true;
});
@ -653,11 +689,9 @@ Zotero.DataObject.prototype.erase = Zotero.Promise.coroutine(function* () {
Zotero.debug('Deleting ' + this.objectType + ' ' + this.id);
yield Zotero.DB.executeTransaction(function* () {
yield this._eraseData(env);
yield this._erasePreCommit(env);
}.bind(this));
Zotero.DB.requireTransaction();
yield this._eraseData(env);
yield this._erasePreCommit(env);
return this._erasePostCommit(env);
});

View file

@ -1173,6 +1173,11 @@ Zotero.Item.prototype.isEditable = function() {
}
Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
// Sanity check
if (!Zotero.DB.inTransaction()) {
throw new Error("Not in transaction saving item " + this.libraryKey);
}
var isNew = env.isNew;
var options = env.options;
@ -1740,6 +1745,11 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
parentItem.clearBestAttachmentState();
}
}
// Sanity check
if (!Zotero.DB.inTransaction()) {
throw new Error("Not in transaction saving item " + this.libraryKey);
}
});
Zotero.Item.prototype._finalizeSave = Zotero.Promise.coroutine(function* (env) {

View file

@ -169,50 +169,48 @@ Zotero.Relations = function () {
* @param {String} prefix
* @param {String[]} ignorePredicates
*/
this.eraseByURIPrefix = function (prefix, ignorePredicates) {
return Zotero.DB.executeTransaction(function* () {
prefix = prefix + '%';
var sql = "SELECT ROWID FROM relations WHERE (subject LIKE ? OR object LIKE ?)";
var params = [prefix, prefix];
if (ignorePredicates) {
for each(var ignorePredicate in ignorePredicates) {
sql += " AND predicate != ?";
params.push(ignorePredicate);
}
this.eraseByURIPrefix = Zotero.Promise.coroutine(function* (prefix, ignorePredicates) {
Zotero.DB.requireTransaction();
prefix = prefix + '%';
var sql = "SELECT ROWID FROM relations WHERE (subject LIKE ? OR object LIKE ?)";
var params = [prefix, prefix];
if (ignorePredicates) {
for each(var ignorePredicate in ignorePredicates) {
sql += " AND predicate != ?";
params.push(ignorePredicate);
}
var ids = yield Zotero.DB.columnQueryAsync(sql, params);
}
var ids = yield Zotero.DB.columnQueryAsync(sql, params);
for (let i=0; i<ids.length; i++) {
let relation = this.get(ids[i]);
yield relation.load();
yield relation.erase();
}
}.bind(this));
}
for (let i=0; i<ids.length; i++) {
let relation = this.get(ids[i]);
yield relation.load();
yield relation.erase();
}
});
/**
* @return {Promise}
*/
this.eraseByURI = function (uri, ignorePredicates) {
return Zotero.DB.executeTransaction(function* () {
var sql = "SELECT ROWID FROM relations WHERE (subject=? OR object=?)";
var params = [uri, uri];
if (ignorePredicates) {
for each(var ignorePredicate in ignorePredicates) {
sql += " AND predicate != ?";
params.push(ignorePredicate);
}
this.eraseByURI = Zotero.Promise.coroutine(function* (uri, ignorePredicates) {
Zotero.DB.requireTransaction();
var sql = "SELECT ROWID FROM relations WHERE (subject=? OR object=?)";
var params = [uri, uri];
if (ignorePredicates) {
for each(var ignorePredicate in ignorePredicates) {
sql += " AND predicate != ?";
params.push(ignorePredicate);
}
var ids = yield Zotero.DB.columnQueryAsync(sql, params);
}
var ids = yield Zotero.DB.columnQueryAsync(sql, params);
for (let i=0; i<ids.length; i++) {
let relation = this.get(ids[i]);
yield relation.load();
yield relation.erase();
}
}.bind(this));
}
for (let i=0; i<ids.length; i++) {
let relation = this.get(ids[i]);
yield relation.load();
yield relation.erase();
}
});
this.purge = Zotero.Promise.coroutine(function* () {

View file

@ -76,29 +76,29 @@ Zotero.Tags = new function() {
/**
* Returns the tagID matching given fields, or creates a new tag and returns its id
*
* @requireTransaction
* @param {Number} libraryID
* @param {String} name - Tag data in API JSON format
* @param {Boolean} [create=false] - If no matching tag, create one
* @return {Promise<Integer>} tagID
*/
this.getIDFromName = Zotero.Promise.method(function (libraryID, name, create) {
this.getIDFromName = Zotero.Promise.coroutine(function* (libraryID, name, create) {
Zotero.DB.requireTransaction();
data = this.cleanData({
tag: name
});
return Zotero.DB.executeTransaction(function* () {
var sql = "SELECT tagID FROM tags WHERE libraryID=? AND name=?";
var id = yield Zotero.DB.valueQueryAsync(sql, [libraryID, data.tag]);
if (!id && create) {
id = yield Zotero.ID.get('tags');
let sql = "INSERT INTO tags (tagID, libraryID, name) VALUES (?, ?, ?)";
let insertID = yield Zotero.DB.queryAsync(sql, [id, libraryID, data.tag]);
if (!id) {
id = insertID;
}
_cacheTag(libraryID, id, data.tag);
var sql = "SELECT tagID FROM tags WHERE libraryID=? AND name=?";
var id = yield Zotero.DB.valueQueryAsync(sql, [libraryID, data.tag]);
if (!id && create) {
id = yield Zotero.ID.get('tags');
let sql = "INSERT INTO tags (tagID, libraryID, name) VALUES (?, ?, ?)";
let insertID = yield Zotero.DB.queryAsync(sql, [id, libraryID, data.tag]);
if (!id) {
id = insertID;
}
return id;
});
_cacheTag(libraryID, id, data.tag);
}
return id;
});

View file

@ -297,6 +297,12 @@ Zotero.DBConnection.prototype.addCallback = function (type, cb) {
}
Zotero.DBConnection.prototype.addCurrentCallback = function (type, cb) {
this.requireTransaction();
this._callbacks.current[type].push(cb);
}
Zotero.DBConnection.prototype.removeCallback = function (type, id) {
switch (type) {
case 'begin':
@ -453,106 +459,83 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
}
}
if ((options.exclusive && this._inTransaction) || this._inExclusiveTransaction) {
yield Zotero.DB.waitForTransaction();
}
var startedTransaction = false;
try {
if (this._inTransaction) {
Zotero.debug("Async DB transaction in progress -- increasing level to "
+ ++this._asyncTransactionNestingLevel, 5);
if (options.onCommit) {
this._callbacks.current.commit.push(options.onCommit);
}
if (options.onRollback) {
this._callbacks.current.rollback.push(options.onRollback);
}
try {
var result = yield Zotero.Promise.coroutine(func)();
}
catch (e) {
Zotero.debug("Rolling back nested async DB transaction", 5);
this._asyncTransactionNestingLevel = 0;
throw e;
}
Zotero.debug("Decreasing async DB transaction level to "
+ --this._asyncTransactionNestingLevel, 5);
return result;
while (this._inTransaction) {
yield Zotero.DB.waitForTransaction().timeout(options.waitTimeout || 10000);
}
else {
Zotero.debug("Beginning async DB transaction", 5);
this._inTransaction = startedTransaction = true;
this._inTransaction = true;
this._inExclusiveTransaction = options.exclusive;
Zotero.debug("Beginning async DB transaction", 5);
this._transactionPromise = new Zotero.Promise(function () {
resolve = arguments[0];
});
this._transactionPromise = new Zotero.Promise(function () {
resolve = arguments[0];
});
// Set a timestamp for this transaction
this._transactionDate = new Date(Math.floor(new Date / 1000) * 1000);
// Set a timestamp for this transaction
this._transactionDate = new Date(Math.floor(new Date / 1000) * 1000);
// Run begin callbacks
for (var i=0; i<this._callbacks.begin.length; i++) {
if (this._callbacks.begin[i]) {
this._callbacks.begin[i]();
}
// Run begin callbacks
for (var i=0; i<this._callbacks.begin.length; i++) {
if (this._callbacks.begin[i]) {
this._callbacks.begin[i]();
}
var conn = this._getConnection(options) || (yield this._getConnectionAsync(options));
var result = yield conn.executeTransaction(func);
Zotero.debug("Committed async DB transaction", 5);
this._inTransaction = false;
// Clear transaction time
if (this._transactionDate) {
this._transactionDate = null;
}
if (options) {
// Function to run once transaction has been committed but before any
// permanent callbacks
if (options.onCommit) {
this._callbacks.current.commit.push(options.onCommit);
}
this._callbacks.current.rollback = [];
if (options.vacuumOnCommit) {
Zotero.debug('Vacuuming database');
yield Zotero.DB.queryAsync('VACUUM');
}
}
// Run temporary commit callbacks
var f;
while (f = this._callbacks.current.commit.shift()) {
yield Zotero.Promise.resolve(f());
}
// Run commit callbacks
for (var i=0; i<this._callbacks.commit.length; i++) {
if (this._callbacks.commit[i]) {
yield this._callbacks.commit[i]();
}
}
return result;
}
var conn = this._getConnection(options) || (yield this._getConnectionAsync(options));
var result = yield conn.executeTransaction(func);
Zotero.debug("Committed async DB transaction", 5);
// Clear transaction time
if (this._transactionDate) {
this._transactionDate = null;
}
if (options.vacuumOnCommit) {
Zotero.debug('Vacuuming database');
yield Zotero.DB.queryAsync('VACUUM');
}
this._inTransaction = false;
// Function to run once transaction has been committed but before any
// permanent callbacks
if (options.onCommit) {
this._callbacks.current.commit.push(options.onCommit);
}
this._callbacks.current.rollback = [];
// Run temporary commit callbacks
var f;
while (f = this._callbacks.current.commit.shift()) {
yield Zotero.Promise.resolve(f());
}
// Run commit callbacks
for (var i=0; i<this._callbacks.commit.length; i++) {
if (this._callbacks.commit[i]) {
yield this._callbacks.commit[i]();
}
}
return result;
}
catch (e) {
Zotero.debug("Rolled back async DB transaction", 5);
Zotero.debug(e, 1);
this._inTransaction = false;
this._inExclusiveTransaction = false;
if (e.name == "TimeoutError") {
Zotero.debug("Timed out waiting for transaction", 1);
}
else {
Zotero.debug("Rolled back async DB transaction", 5);
Zotero.debug(e, 1);
}
if (startedTransaction) {
this._inTransaction = false;
}
if (options) {
// Function to run once transaction has been committed but before any
// permanent callbacks
if (options.onRollback) {
this._callbacks.current.rollback.push(options.onRollback);
}
// Function to run once transaction has been committed but before any
// permanent callbacks
if (options.onRollback) {
this._callbacks.current.rollback.push(options.onRollback);
}
// Run temporary commit callbacks
@ -586,15 +569,28 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
});
Zotero.DBConnection.prototype.inTransaction = function () {
return this._inTransaction;
}
Zotero.DBConnection.prototype.waitForTransaction = function () {
if (!this._inTransaction) {
return Zotero.Promise.resolve();
return Zotero.Promise.resolve().cancellable();
}
Zotero.debug("Waiting for transaction to finish");
Zotero.debug((new Error).stack);
return this._transactionPromise;
};
Zotero.DBConnection.prototype.requireTransaction = function () {
if (!this._inTransaction) {
throw new Error("Not in transaction");
}
};
/**
* @param {String} sql SQL statement to run
* @param {Array|String|Integer} [params] SQL parameters to bind
@ -818,7 +814,7 @@ Zotero.DBConnection.prototype.tableExists = function (table) {
*
* @return {Promise}
*/
Zotero.DBConnection.prototype.executeSQLFile = function (sql) {
Zotero.DBConnection.prototype.executeSQLFile = Zotero.Promise.coroutine(function* (sql) {
var nonCommentRE = /^[^-]/;
var trailingCommentRE = /^(.*?)(?:--.+)?$/;
@ -836,13 +832,13 @@ Zotero.DBConnection.prototype.executeSQLFile = function (sql) {
var statements = sql.split(";")
.map(function (x) x.replace(/TEMPSEMI/g, ";"));
return this.executeTransaction(function* () {
var statement;
while (statement = statements.shift()) {
yield Zotero.DB.queryAsync(statement);
}
});
}
this.requireTransaction();
var statement;
while (statement = statements.shift()) {
yield Zotero.DB.queryAsync(statement);
}
});
/*

View file

@ -403,25 +403,25 @@ Zotero.Fulltext = new function(){
/**
* Index multiple words at once
*
* @requireTransaction
* @param {Number} itemID
* @param {Array<string>} words
* @return {Promise}
*/
function indexWords(itemID, words) {
var indexWords = Zotero.Promise.coroutine(function* (itemID, words) {
Zotero.DB.requireTransaction();
let chunk;
return Zotero.DB.executeTransaction(function* () {
yield Zotero.DB.queryAsync("DELETE FROM indexing.fulltextWords");
while (words.length > 0) {
chunk = words.splice(0, 100);
Zotero.DB.queryAsync('INSERT INTO indexing.fulltextWords (word) ' + ['SELECT ?' for (word of chunk)].join(' UNION '), chunk);
}
yield Zotero.DB.queryAsync('INSERT OR IGNORE INTO fulltextWords (word) SELECT word FROM indexing.fulltextWords');
yield Zotero.DB.queryAsync('DELETE FROM fulltextItemWords WHERE itemID = ?', [itemID]);
yield Zotero.DB.queryAsync('INSERT OR IGNORE INTO fulltextItemWords (wordID, itemID) SELECT wordID, ? FROM fulltextWords JOIN indexing.fulltextWords USING(word)', [itemID]);
yield Zotero.DB.queryAsync("REPLACE INTO fulltextItems (itemID, version) VALUES (?,?)", [itemID, 0]);
yield Zotero.DB.queryAsync("DELETE FROM indexing.fulltextWords");
}.bind(this));
}
yield Zotero.DB.queryAsync("DELETE FROM indexing.fulltextWords");
while (words.length > 0) {
chunk = words.splice(0, 100);
Zotero.DB.queryAsync('INSERT INTO indexing.fulltextWords (word) ' + ['SELECT ?' for (word of chunk)].join(' UNION '), chunk);
}
yield Zotero.DB.queryAsync('INSERT OR IGNORE INTO fulltextWords (word) SELECT word FROM indexing.fulltextWords');
yield Zotero.DB.queryAsync('DELETE FROM fulltextItemWords WHERE itemID = ?', [itemID]);
yield Zotero.DB.queryAsync('INSERT OR IGNORE INTO fulltextItemWords (wordID, itemID) SELECT wordID, ? FROM fulltextWords JOIN indexing.fulltextWords USING(word)', [itemID]);
yield Zotero.DB.queryAsync("REPLACE INTO fulltextItems (itemID, version) VALUES (?,?)", [itemID, 0]);
yield Zotero.DB.queryAsync("DELETE FROM indexing.fulltextWords");
});
/**
@ -559,15 +559,13 @@ Zotero.Fulltext = new function(){
}
}
yield Zotero.DB.executeTransaction(function* () {
yield indexString(text, charset, itemID);
yield indexString(text, charset, itemID);
// Record the number of characters indexed (unless we're indexing a (PDF) cache file,
// in which case the stats are coming from elsewhere)
if (!isCacheFile) {
yield setChars(itemID, { indexed: text.length, total: totalChars });
}
});
// Record the number of characters indexed (unless we're indexing a (PDF) cache file,
// in which case the stats are coming from elsewhere)
if (!isCacheFile) {
yield setChars(itemID, { indexed: text.length, total: totalChars });
}
return true;
}.bind(this));
@ -690,10 +688,8 @@ Zotero.Fulltext = new function(){
return false;
}
yield Zotero.DB.executeTransaction(function* () {
yield indexFile(cacheFilePath, 'text/plain', 'utf-8', itemID, true, true);
yield setPages(itemID, { indexed: pagesIndexed, total: totalPages });
});
yield indexFile(cacheFilePath, 'text/plain', 'utf-8', itemID, true, true);
yield setPages(itemID, { indexed: pagesIndexed, total: totalPages });
return true;
});
@ -827,7 +823,9 @@ Zotero.Fulltext = new function(){
+ libraryKey, 2);
// Delete rows for items that weren't supposed to be indexed
yield this.clearItemWords(itemID);
yield Zotero.DB.executeTransaction(function* () {
yield this.clearItemWords(itemID);
}.bind(this));
continue;
}
@ -1291,16 +1289,18 @@ Zotero.Fulltext = new function(){
});
/**
* @requireTransaction
*/
this.clearItemWords = Zotero.Promise.coroutine(function* (itemID, skipCacheClear) {
var indexed = yield Zotero.DB.executeTransaction(function* () {
var sql = "SELECT rowid FROM fulltextItems WHERE itemID=? LIMIT 1";
var indexed = yield Zotero.DB.valueQueryAsync(sql, itemID);
if (indexed) {
yield Zotero.DB.queryAsync("DELETE FROM fulltextItemWords WHERE itemID=?", itemID);
yield Zotero.DB.queryAsync("DELETE FROM fulltextItems WHERE itemID=?", itemID);
}
return indexed;
}.bind(this));
Zotero.DB.requireTransaction();
var sql = "SELECT rowid FROM fulltextItems WHERE itemID=? LIMIT 1";
var indexed = yield Zotero.DB.valueQueryAsync(sql, itemID);
if (indexed) {
yield Zotero.DB.queryAsync("DELETE FROM fulltextItemWords WHERE itemID=?", itemID);
yield Zotero.DB.queryAsync("DELETE FROM fulltextItems WHERE itemID=?", itemID);
}
if (indexed) {
Zotero.Prefs.set('purge.fulltext', true);

View file

@ -123,21 +123,19 @@ Zotero.Schema = new function(){
// Update custom tables if they exist so that changes are in
// place before user data migration
if (Zotero.DB.tableExists('customItemTypes')) {
yield Zotero.Schema.updateCustomTables(updated);
yield _updateCustomTables(updated);
}
updated = yield _migrateUserDataSchema(userdata);
yield _updateSchema('triggers');
// Populate combined tables for custom types and fields -- this is likely temporary
//
// We do this again in case custom fields were changed during user data migration
yield _updateCustomTables()
return updated;
}.bind(this));
// Populate combined tables for custom types and fields
// -- this is likely temporary
//
// We do this even if updated in case custom fields were
// changed during user data migration
yield Zotero.Schema.updateCustomTables()
if (updated) {
// Upgrade seems to have been a success -- delete any previous backups
var maxPrevious = userdata - 1;
@ -318,7 +316,7 @@ Zotero.Schema = new function(){
});
var _reloadSchema = Zotero.Promise.coroutine(function* () {
yield Zotero.Schema.updateCustomTables();
yield _updateCustomTables();
yield Zotero.ItemTypes.load();
yield Zotero.ItemFields.load();
yield Zotero.SearchConditions.init();
@ -335,58 +333,58 @@ Zotero.Schema = new function(){
});
this.updateCustomTables = function (skipDelete, skipSystem) {
return Zotero.DB.executeTransaction(function* (conn) {
Zotero.debug("Updating custom tables");
var _updateCustomTables = Zotero.Promise.coroutine(function* (skipDelete, skipSystem) {
Zotero.debug("Updating custom tables");
if (!skipDelete) {
yield Zotero.DB.queryAsync("DELETE FROM itemTypesCombined");
yield Zotero.DB.queryAsync("DELETE FROM fieldsCombined WHERE fieldID NOT IN (SELECT fieldID FROM itemData)");
yield Zotero.DB.queryAsync("DELETE FROM itemTypeFieldsCombined");
yield Zotero.DB.queryAsync("DELETE FROM baseFieldMappingsCombined");
}
Zotero.DB.requireTransaction();
var offset = Zotero.ItemTypes.customIDOffset;
yield Zotero.DB.queryAsync(
"INSERT INTO itemTypesCombined "
+ (
skipSystem
? ""
: "SELECT itemTypeID, typeName, display, 0 AS custom FROM itemTypes UNION "
)
+ "SELECT customItemTypeID + " + offset + " AS itemTypeID, typeName, display, 1 AS custom FROM customItemTypes"
);
yield Zotero.DB.queryAsync(
"INSERT OR IGNORE INTO fieldsCombined "
+ (
skipSystem
? ""
: "SELECT fieldID, fieldName, NULL AS label, fieldFormatID, 0 AS custom FROM fields UNION "
)
+ "SELECT customFieldID + " + offset + " AS fieldID, fieldName, label, NULL, 1 AS custom FROM customFields"
);
yield Zotero.DB.queryAsync(
"INSERT INTO itemTypeFieldsCombined "
+ (
skipSystem
? ""
: "SELECT itemTypeID, fieldID, hide, orderIndex FROM itemTypeFields UNION "
)
+ "SELECT customItemTypeID + " + offset + " AS itemTypeID, "
+ "COALESCE(fieldID, customFieldID + " + offset + ") AS fieldID, hide, orderIndex FROM customItemTypeFields"
);
yield Zotero.DB.queryAsync(
"INSERT INTO baseFieldMappingsCombined "
+ (
skipSystem
? ""
: "SELECT itemTypeID, baseFieldID, fieldID FROM baseFieldMappings UNION "
)
+ "SELECT customItemTypeID + " + offset + " AS itemTypeID, baseFieldID, "
+ "customFieldID + " + offset + " AS fieldID FROM customBaseFieldMappings"
);
});
}
if (!skipDelete) {
yield Zotero.DB.queryAsync("DELETE FROM itemTypesCombined");
yield Zotero.DB.queryAsync("DELETE FROM fieldsCombined WHERE fieldID NOT IN (SELECT fieldID FROM itemData)");
yield Zotero.DB.queryAsync("DELETE FROM itemTypeFieldsCombined");
yield Zotero.DB.queryAsync("DELETE FROM baseFieldMappingsCombined");
}
var offset = Zotero.ItemTypes.customIDOffset;
yield Zotero.DB.queryAsync(
"INSERT INTO itemTypesCombined "
+ (
skipSystem
? ""
: "SELECT itemTypeID, typeName, display, 0 AS custom FROM itemTypes UNION "
)
+ "SELECT customItemTypeID + " + offset + " AS itemTypeID, typeName, display, 1 AS custom FROM customItemTypes"
);
yield Zotero.DB.queryAsync(
"INSERT OR IGNORE INTO fieldsCombined "
+ (
skipSystem
? ""
: "SELECT fieldID, fieldName, NULL AS label, fieldFormatID, 0 AS custom FROM fields UNION "
)
+ "SELECT customFieldID + " + offset + " AS fieldID, fieldName, label, NULL, 1 AS custom FROM customFields"
);
yield Zotero.DB.queryAsync(
"INSERT INTO itemTypeFieldsCombined "
+ (
skipSystem
? ""
: "SELECT itemTypeID, fieldID, hide, orderIndex FROM itemTypeFields UNION "
)
+ "SELECT customItemTypeID + " + offset + " AS itemTypeID, "
+ "COALESCE(fieldID, customFieldID + " + offset + ") AS fieldID, hide, orderIndex FROM customItemTypeFields"
);
yield Zotero.DB.queryAsync(
"INSERT INTO baseFieldMappingsCombined "
+ (
skipSystem
? ""
: "SELECT itemTypeID, baseFieldID, fieldID FROM baseFieldMappings UNION "
)
+ "SELECT customItemTypeID + " + offset + " AS itemTypeID, baseFieldID, "
+ "customFieldID + " + offset + " AS fieldID FROM customBaseFieldMappings"
);
});
/**
@ -1454,7 +1452,7 @@ Zotero.Schema = new function(){
yield _getSchemaSQL('triggers').then(function (sql) {
return Zotero.DB.executeSQLFile(sql);
});
yield Zotero.Schema.updateCustomTables(true);
yield _updateCustomTables(true);
yield _getSchemaSQLVersion('system').then(function (version) {
return _updateDBVersion('system', version);
@ -1918,363 +1916,363 @@ Zotero.Schema = new function(){
//
// If libraryID set, make sure no relations still use a local user key, and then remove on-error code in sync.js
function _migrateUserDataSchema(fromVersion) {
return _getSchemaSQLVersion('userdata')
.then(function (toVersion) {
if (fromVersion >= toVersion) {
return false;
var _migrateUserDataSchema = Zotero.Promise.coroutine(function* (fromVersion) {
var toVersion = yield _getSchemaSQLVersion('userdata');
if (fromVersion >= toVersion) {
return false;
}
Zotero.debug('Updating user data tables from version ' + fromVersion + ' to ' + toVersion);
Zotero.DB.requireTransaction();
// Step through version changes until we reach the current version
//
// Each block performs the changes necessary to move from the
// previous revision to that one.
for (let i = fromVersion + 1; i <= toVersion; i++) {
if (i == 80) {
yield _updateDBVersion('compatibility', 1);
yield Zotero.DB.queryAsync("INSERT INTO libraries VALUES (1, 'user')");
yield Zotero.DB.queryAsync("INSERT INTO libraries VALUES (2, 'publications')");
let oldUserLibraryID = yield Zotero.DB.valueQueryAsync("SELECT value FROM settings WHERE setting='account' AND key='libraryID'");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO syncObjectTypes VALUES (7, 'setting')");
yield Zotero.DB.queryAsync("DELETE FROM version WHERE schema IN ('userdata2', 'userdata3')");
yield Zotero.DB.queryAsync("ALTER TABLE libraries ADD COLUMN version INT NOT NULL DEFAULT 0");
yield Zotero.DB.queryAsync("ALTER TABLE libraries ADD COLUMN lastsync INT NOT NULL DEFAULT 0");
yield Zotero.DB.queryAsync("CREATE TABLE syncCache (\n libraryID INT NOT NULL,\n key TEXT NOT NULL,\n syncObjectTypeID INT NOT NULL,\n version INT NOT NULL,\n data TEXT,\n PRIMARY KEY (libraryID, key, syncObjectTypeID),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE,\n FOREIGN KEY (syncObjectTypeID) REFERENCES syncObjectTypes(syncObjectTypeID)\n)");
yield Zotero.DB.queryAsync("DROP TABLE translatorCache");
yield Zotero.DB.queryAsync("CREATE TABLE translatorCache (\n fileName TEXT PRIMARY KEY,\n metadataJSON TEXT,\n lastModifiedTime INT\n);");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_annotations_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_annotations_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_annotations_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemAttachments_itemID_annotations_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_collections_parentCollectionID_collections_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_collections_parentCollectionID_collections_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_collections_parentCollectionID_collections_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_collections_collectionID_collections_parentCollectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_collectionItems_collectionID_collections_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_collectionItems_collectionID_collections_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_collectionItems_collectionID_collections_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_collections_collectionID_collectionItems_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_collectionItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_collectionItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_collectionItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_collectionItems_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_creators_creatorDataID_creatorData_creatorDataID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_creators_creatorDataID_creatorData_creatorDataID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_creators_creatorDataID_creatorData_creatorDataID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_creatorData_creatorDataID_creators_creatorDataID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_customBaseFieldMappings_customItemTypeID_customItemTypes_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customBaseFieldMappings_customItemTypeID_customItemTypes_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_customBaseFieldMappings_customItemTypeID_customItemTypes_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customItemTypes_customItemTypeID_customBaseFieldMappings_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_customBaseFieldMappings_baseFieldID_fields_fieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customBaseFieldMappings_baseFieldID_fields_fieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_customBaseFieldMappings_customFieldID_customFields_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customBaseFieldMappings_customFieldID_customFields_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_customBaseFieldMappings_customFieldID_customFields_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customFields_customFieldID_customBaseFieldMappings_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_customItemTypeFields_customItemTypeID_customItemTypes_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customItemTypeFields_customItemTypeID_customItemTypes_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_customItemTypeFields_customItemTypeID_customItemTypes_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customItemTypes_customItemTypeID_customItemTypeFields_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_customItemTypeFields_fieldID_fields_fieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customItemTypeFields_fieldID_fields_fieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customItemTypeFields_customFieldID_customFields_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_customItemTypeFields_customFieldID_customFields_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customFields_customFieldID_customItemTypeFields_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_fulltextItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_fulltextItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_fulltextItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_fulltextItems_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_fulltextItemWords_wordID_fulltextWords_wordID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_fulltextItemWords_wordID_fulltextWords_wordID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_fulltextItemWords_wordID_fulltextWords_wordID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_fulltextWords_wordID_fulltextItemWords_wordID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_fulltextItemWords_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_fulltextItemWords_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_fulltextItemWords_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_fulltextItemWords_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_groups_libraryID_libraries_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_groups_libraryID_libraries_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_groups_libraryID_libraries_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_libraries_libraryID_groups_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_groupItems_createdByUserID_users_userID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_groupItems_createdByUserID_users_userID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_groupItems_createdByUserID_users_userID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_users_userID_groupItems_createdByUserID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_groupItems_lastModifiedByUserID_users_userID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_groupItems_lastModifiedByUserID_users_userID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_groupItems_lastModifiedByUserID_users_userID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_users_userID_groupItems_lastModifiedByUserID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_highlights_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_highlights_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_highlights_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemAttachments_itemID_highlights_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemAttachments_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemAttachments_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemAttachments_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemAttachments_sourceItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemAttachments_sourceItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemAttachments_sourceItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemAttachments_sourceItemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemCreators_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemCreators_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemCreators_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemCreators_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemCreators_creatorID_creators_creatorID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemCreators_creatorID_creators_creatorID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemCreators_creatorID_creators_creatorID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_creators_creatorID_itemCreators_creatorID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemCreators_creatorTypeID_creatorTypes_creatorTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemCreators_creatorTypeID_creatorTypes_creatorTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemCreators_creatorTypeID_creatorTypes_creatorTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_creatorTypes_creatorTypeID_itemCreators_creatorTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemCreators_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemCreators_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemData_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemData_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemData_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemData_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemData_fieldID_fields_fieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemData_fieldID_fields_fieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemData_valueID_itemDataValues_valueID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemData_valueID_itemDataValues_valueID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemData_valueID_itemDataValues_valueID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemDataValues_valueID_itemData_valueID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemNotes_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemNotes_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemNotes_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemNotes_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemNotes_sourceItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemNotes_sourceItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemNotes_sourceItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemNotes_sourceItemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_items_libraryID_libraries_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_libraryID_libraries_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_items_libraryID_libraries_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_libraries_libraryID_items_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemSeeAlso_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemSeeAlso_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemSeeAlso_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemSeeAlso_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemSeeAlso_linkedItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemSeeAlso_linkedItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemSeeAlso_linkedItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemSeeAlso_linkedItemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemTags_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemTags_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemTags_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_items_itemID_itemTags_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemTags_tagID_tags_tagID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemTags_tagID_tags_tagID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemTags_tagID_tags_tagID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_tags_tagID_itemTags_tagID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_savedSearchConditions_savedSearchID_savedSearches_savedSearchID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_savedSearchConditions_savedSearchID_savedSearches_savedSearchID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_savedSearchConditions_savedSearchID_savedSearches_savedSearchID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_savedSearches_savedSearchID_savedSearchConditions_savedSearchID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_deletedItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_deletedItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_deletedItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_deletedItems_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_syncDeleteLog_syncObjectTypeID_syncObjectTypes_syncObjectTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_syncDeleteLog_syncObjectTypeID_syncObjectTypes_syncObjectTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_syncDeleteLog_syncObjectTypeID_syncObjectTypes_syncObjectTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_syncObjectTypes_syncObjectTypeID_syncDeleteLog_syncObjectTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_proxyHosts_proxyID_proxies_proxyID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_proxyHosts_proxyID_proxies_proxyID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_proxyHosts_proxyID_proxies_proxyID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_proxies_proxyID_proxyHosts_proxyID");
yield Zotero.DB.queryAsync("ALTER TABLE collections RENAME TO collectionsOld");
yield Zotero.DB.queryAsync("CREATE TABLE collections (\n collectionID INTEGER PRIMARY KEY,\n collectionName TEXT NOT NULL,\n parentCollectionID INT DEFAULT NULL,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n libraryID INT NOT NULL,\n key TEXT NOT NULL,\n version INT NOT NULL DEFAULT 0,\n synced INT NOT NULL DEFAULT 0,\n UNIQUE (libraryID, key),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE,\n FOREIGN KEY (parentCollectionID) REFERENCES collections(collectionID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO collections SELECT collectionID, collectionName, parentCollectionID, clientDateModified, IFNULL(libraryID, 1), key, 0, 0 FROM collectionsOld ORDER BY collectionID DESC");
yield Zotero.DB.queryAsync("CREATE INDEX collections_synced ON collections(synced)");
yield Zotero.DB.queryAsync("ALTER TABLE items RENAME TO itemsOld");
yield Zotero.DB.queryAsync("CREATE TABLE items (\n itemID INTEGER PRIMARY KEY,\n itemTypeID INT NOT NULL,\n dateAdded TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n dateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n libraryID INT NOT NULL,\n key TEXT NOT NULL,\n version INT NOT NULL DEFAULT 0,\n synced INT NOT NULL DEFAULT 0,\n UNIQUE (libraryID, key),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO items SELECT itemID, itemTypeID, dateAdded, dateModified, clientDateModified, IFNULL(libraryID, 1), key, 0, 0 FROM itemsOld ORDER BY dateAdded DESC");
yield Zotero.DB.queryAsync("CREATE INDEX items_synced ON items(synced)");
yield Zotero.DB.queryAsync("ALTER TABLE creators RENAME TO creatorsOld");
yield Zotero.DB.queryAsync("CREATE TABLE creators (\n creatorID INTEGER PRIMARY KEY,\n firstName TEXT,\n lastName TEXT,\n fieldMode INT,\n UNIQUE (lastName, firstName, fieldMode)\n)");
yield Zotero.DB.queryAsync("INSERT INTO creators SELECT creatorDataID, firstName, lastName, fieldMode FROM creatorData");
yield Zotero.DB.queryAsync("ALTER TABLE itemCreators RENAME TO itemCreatorsOld");
yield Zotero.DB.queryAsync("CREATE TABLE itemCreators (\n itemID INT NOT NULL,\n creatorID INT NOT NULL,\n creatorTypeID INT NOT NULL DEFAULT 1,\n orderIndex INT NOT NULL DEFAULT 0,\n PRIMARY KEY (itemID, creatorID, creatorTypeID, orderIndex),\n UNIQUE (itemID, orderIndex),\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (creatorID) REFERENCES creators(creatorID) ON DELETE CASCADE,\n FOREIGN KEY (creatorTypeID) REFERENCES creatorTypes(creatorTypeID)\n)");
yield Zotero.DB.queryAsync("CREATE INDEX itemCreators_creatorTypeID ON itemCreators(creatorTypeID)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO itemCreators SELECT itemID, C.creatorID, creatorTypeID, orderIndex FROM itemCreatorsOld ICO JOIN creatorsOld CO USING (creatorID) JOIN creators C ON (CO.creatorDataID=C.creatorID)");
yield Zotero.DB.queryAsync("ALTER TABLE savedSearches RENAME TO savedSearchesOld");
yield Zotero.DB.queryAsync("CREATE TABLE savedSearches (\n savedSearchID INTEGER PRIMARY KEY,\n savedSearchName TEXT NOT NULL,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n libraryID INT NOT NULL,\n key TEXT NOT NULL,\n version INT NOT NULL DEFAULT 0,\n synced INT NOT NULL DEFAULT 0,\n UNIQUE (libraryID, key),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO savedSearches SELECT savedSearchID, savedSearchName, clientDateModified, IFNULL(libraryID, 1), key, 0, 0 FROM savedSearchesOld ORDER BY savedSearchID DESC");
yield Zotero.DB.queryAsync("CREATE INDEX savedSearches_synced ON savedSearches(synced)");
yield Zotero.DB.queryAsync("ALTER TABLE tags RENAME TO tagsOld");
yield Zotero.DB.queryAsync("CREATE TABLE tags (\n tagID INTEGER PRIMARY KEY,\n libraryID INT NOT NULL,\n name TEXT NOT NULL,\n UNIQUE (libraryID, name)\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO tags SELECT tagID, IFNULL(libraryID, 1), name FROM tagsOld");
yield Zotero.DB.queryAsync("ALTER TABLE itemTags RENAME TO itemTagsOld");
yield Zotero.DB.queryAsync("CREATE TABLE itemTags (\n itemID INT NOT NULL,\n tagID INT NOT NULL,\n type INT NOT NULL,\n PRIMARY KEY (itemID, tagID),\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (tagID) REFERENCES tags(tagID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO itemTags SELECT itemID, T.tagID, TOld.type FROM itemTagsOld ITO JOIN tagsOld TOld USING (tagID) JOIN tags T ON (IFNULL(TOld.libraryID, 1)=T.libraryID AND TOld.name=T.name COLLATE BINARY)");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS itemTags_tagID");
yield Zotero.DB.queryAsync("CREATE INDEX itemTags_tagID ON itemTags(tagID)");
yield Zotero.DB.queryAsync("CREATE TABLE IF NOT EXISTS syncedSettings (\n setting TEXT NOT NULL,\n libraryID INT NOT NULL,\n value NOT NULL,\n version INT NOT NULL DEFAULT 0,\n synced INT NOT NULL DEFAULT 0,\n PRIMARY KEY (setting, libraryID)\n)");
yield Zotero.DB.queryAsync("ALTER TABLE syncedSettings RENAME TO syncedSettingsOld");
yield Zotero.DB.queryAsync("CREATE TABLE syncedSettings (\n setting TEXT NOT NULL,\n libraryID INT NOT NULL,\n value NOT NULL,\n version INT NOT NULL DEFAULT 0,\n synced INT NOT NULL DEFAULT 0,\n PRIMARY KEY (setting, libraryID),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("UPDATE syncedSettingsOld SET libraryID=1 WHERE libraryID=0");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO syncedSettings SELECT * FROM syncedSettingsOld");
yield Zotero.DB.queryAsync("ALTER TABLE itemData RENAME TO itemDataOld");
yield Zotero.DB.queryAsync("CREATE TABLE itemData (\n itemID INT,\n fieldID INT,\n valueID,\n PRIMARY KEY (itemID, fieldID),\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (fieldID) REFERENCES fieldsCombined(fieldID),\n FOREIGN KEY (valueID) REFERENCES itemDataValues(valueID)\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO itemData SELECT * FROM itemDataOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS itemData_fieldID");
yield Zotero.DB.queryAsync("CREATE INDEX itemData_fieldID ON itemData(fieldID)");
yield Zotero.DB.queryAsync("ALTER TABLE itemNotes RENAME TO itemNotesOld");
yield Zotero.DB.queryAsync("CREATE TABLE itemNotes (\n itemID INTEGER PRIMARY KEY,\n parentItemID INT,\n note TEXT,\n title TEXT,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (parentItemID) REFERENCES items(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO itemNotes SELECT * FROM itemNotesOld");
yield Zotero.DB.queryAsync("CREATE INDEX itemNotes_parentItemID ON itemNotes(parentItemID)");
yield Zotero.DB.queryAsync("ALTER TABLE itemAttachments RENAME TO itemAttachmentsOld");
yield Zotero.DB.queryAsync("CREATE TABLE itemAttachments (\n itemID INTEGER PRIMARY KEY,\n parentItemID INT,\n linkMode INT,\n contentType TEXT,\n charsetID INT,\n path TEXT,\n syncState INT DEFAULT 0,\n storageModTime INT,\n storageHash TEXT,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (parentItemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (charsetID) REFERENCES charsets(charsetID) ON DELETE SET NULL\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO itemAttachments SELECT itemID, sourceItemID, linkMode, mimeType, charsetID, path, syncState, storageModTime, storageHash FROM itemAttachmentsOld");
yield Zotero.DB.queryAsync("CREATE INDEX itemAttachments_parentItemID ON itemAttachments(parentItemID)");
yield Zotero.DB.queryAsync("CREATE INDEX itemAttachments_charsetID ON itemAttachments(charsetID)");
yield Zotero.DB.queryAsync("CREATE INDEX itemAttachments_contentType ON itemAttachments(contentType)");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS itemAttachments_syncState");
yield Zotero.DB.queryAsync("CREATE INDEX itemAttachments_syncState ON itemAttachments(syncState)");
yield Zotero.DB.queryAsync("ALTER TABLE itemSeeAlso RENAME TO itemSeeAlsoOld");
yield Zotero.DB.queryAsync("CREATE TABLE itemSeeAlso (\n itemID INT NOT NULL,\n linkedItemID INT NOT NULL,\n PRIMARY KEY (itemID, linkedItemID),\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (linkedItemID) REFERENCES items(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO itemSeeAlso SELECT * FROM itemSeeAlsoOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS itemSeeAlso_linkedItemID");
yield Zotero.DB.queryAsync("CREATE INDEX itemSeeAlso_linkedItemID ON itemSeeAlso(linkedItemID)");
yield Zotero.DB.queryAsync("ALTER TABLE collectionItems RENAME TO collectionItemsOld");
yield Zotero.DB.queryAsync("CREATE TABLE collectionItems (\n collectionID INT NOT NULL,\n itemID INT NOT NULL,\n orderIndex INT NOT NULL DEFAULT 0,\n PRIMARY KEY (collectionID, itemID),\n FOREIGN KEY (collectionID) REFERENCES collections(collectionID) ON DELETE CASCADE,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO collectionItems SELECT * FROM collectionItemsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS itemID"); // incorrect old name
yield Zotero.DB.queryAsync("CREATE INDEX collectionItems_itemID ON collectionItems(itemID)");
yield Zotero.DB.queryAsync("ALTER TABLE savedSearchConditions RENAME TO savedSearchConditionsOld");
yield Zotero.DB.queryAsync("CREATE TABLE savedSearchConditions (\n savedSearchID INT NOT NULL,\n searchConditionID INT NOT NULL,\n condition TEXT NOT NULL,\n operator TEXT,\n value TEXT,\n required NONE,\n PRIMARY KEY (savedSearchID, searchConditionID),\n FOREIGN KEY (savedSearchID) REFERENCES savedSearches(savedSearchID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO savedSearchConditions SELECT * FROM savedSearchConditionsOld");
yield Zotero.DB.queryAsync("DROP TABLE savedSearchConditionsOld");
yield Zotero.DB.queryAsync("ALTER TABLE deletedItems RENAME TO deletedItemsOld");
yield Zotero.DB.queryAsync("CREATE TABLE deletedItems (\n itemID INTEGER PRIMARY KEY,\n dateDeleted DEFAULT CURRENT_TIMESTAMP NOT NULL,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO deletedItems SELECT * FROM deletedItemsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS deletedItems_dateDeleted");
yield Zotero.DB.queryAsync("CREATE INDEX deletedItems_dateDeleted ON deletedItems(dateDeleted)");
yield Zotero.DB.queryAsync("UPDATE relations SET libraryID=1 WHERE libraryID=?", oldUserLibraryID);
yield Zotero.DB.queryAsync("ALTER TABLE relations RENAME TO relationsOld");
yield Zotero.DB.queryAsync("CREATE TABLE relations (\n libraryID INT NOT NULL,\n subject TEXT NOT NULL,\n predicate TEXT NOT NULL,\n object TEXT NOT NULL,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (subject, predicate, object),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO relations SELECT * FROM relationsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS relations_object");
yield Zotero.DB.queryAsync("CREATE INDEX relations_object ON relations(object)");
yield Zotero.DB.queryAsync("ALTER TABLE groups RENAME TO groupsOld");
yield Zotero.DB.queryAsync("CREATE TABLE groups (\n groupID INTEGER PRIMARY KEY,\n libraryID INT NOT NULL UNIQUE,\n name TEXT NOT NULL,\n description TEXT NOT NULL,\n editable INT NOT NULL,\n filesEditable INT NOT NULL,\n version INT NOT NULL,\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO groups SELECT groupID, libraryID, name, description, editable, filesEditable, 0 FROM groupsOld");
yield Zotero.DB.queryAsync("ALTER TABLE groupItems RENAME TO groupItemsOld");
yield Zotero.DB.queryAsync("CREATE TABLE groupItems (\n itemID INTEGER PRIMARY KEY,\n createdByUserID INT,\n lastModifiedByUserID INT,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (createdByUserID) REFERENCES users(userID) ON DELETE SET NULL,\n FOREIGN KEY (lastModifiedByUserID) REFERENCES users(userID) ON DELETE SET NULL\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO groupItems SELECT * FROM groupItemsOld");
let cols = yield Zotero.DB.getColumns('fulltextItems');
if (cols.indexOf("synced") == -1) {
Zotero.DB.queryAsync("ALTER TABLE fulltextItems ADD COLUMN synced INT DEFAULT 0");
Zotero.DB.queryAsync("REPLACE INTO settings (setting, key, value) VALUES ('fulltext', 'downloadAll', 1)");
}
yield Zotero.DB.queryAsync("ALTER TABLE fulltextItems RENAME TO fulltextItemsOld");
yield Zotero.DB.queryAsync("CREATE TABLE fulltextItems (\n itemID INTEGER PRIMARY KEY,\n version INT,\n indexedPages INT,\n totalPages INT,\n indexedChars INT,\n totalChars INT,\n synced INT DEFAULT 0,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO fulltextItems SELECT * FROM fulltextItemsOld");
yield Zotero.DB.queryAsync("ALTER TABLE fulltextItemWords RENAME TO fulltextItemWordsOld");
yield Zotero.DB.queryAsync("CREATE TABLE fulltextItemWords (\n wordID INT,\n itemID INT,\n PRIMARY KEY (wordID, itemID),\n FOREIGN KEY (wordID) REFERENCES fulltextWords(wordID),\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO fulltextItemWords SELECT * FROM fulltextItemWordsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS fulltextItems_version");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS fulltextItemWords_itemID");
yield Zotero.DB.queryAsync("CREATE INDEX fulltextItems_version ON fulltextItems(version)");
yield Zotero.DB.queryAsync("CREATE INDEX fulltextItemWords_itemID ON fulltextItemWords(itemID)");
yield Zotero.DB.queryAsync("UPDATE syncDeleteLog SET libraryID=1 WHERE libraryID=0");
yield Zotero.DB.queryAsync("ALTER TABLE syncDeleteLog RENAME TO syncDeleteLogOld");
yield Zotero.DB.queryAsync("CREATE TABLE syncDeleteLog (\n syncObjectTypeID INT NOT NULL,\n libraryID INT NOT NULL,\n key TEXT NOT NULL,\n synced INT NOT NULL DEFAULT 0,\n UNIQUE (syncObjectTypeID, libraryID, key),\n FOREIGN KEY (syncObjectTypeID) REFERENCES syncObjectTypes(syncObjectTypeID),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO syncDeleteLog SELECT * FROM syncDeleteLogOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS syncDeleteLog_timestamp");
yield Zotero.DB.queryAsync("CREATE INDEX syncDeleteLog_synced ON syncDeleteLog(synced)");
yield Zotero.DB.queryAsync("ALTER TABLE storageDeleteLog RENAME TO storageDeleteLogOld");
yield Zotero.DB.queryAsync("CREATE TABLE storageDeleteLog (\n libraryID INT NOT NULL,\n key TEXT NOT NULL,\n synced INT NOT NULL DEFAULT 0,\n PRIMARY KEY (libraryID, key),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO storageDeleteLog SELECT * FROM storageDeleteLogOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS storageDeleteLog_timestamp");
yield Zotero.DB.queryAsync("CREATE INDEX storageDeleteLog_synced ON storageDeleteLog(synced)");
yield Zotero.DB.queryAsync("ALTER TABLE annotations RENAME TO annotationsOld");
yield Zotero.DB.queryAsync("CREATE TABLE annotations (\n annotationID INTEGER PRIMARY KEY,\n itemID INT NOT NULL,\n parent TEXT,\n textNode INT,\n offset INT,\n x INT,\n y INT,\n cols INT,\n rows INT,\n text TEXT,\n collapsed BOOL,\n dateModified DATE,\n FOREIGN KEY (itemID) REFERENCES itemAttachments(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO annotations SELECT * FROM annotationsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS annotations_itemID");
yield Zotero.DB.queryAsync("CREATE INDEX annotations_itemID ON annotations(itemID)");
yield Zotero.DB.queryAsync("ALTER TABLE highlights RENAME TO highlightsOld");
yield Zotero.DB.queryAsync("CREATE TABLE highlights (\n highlightID INTEGER PRIMARY KEY,\n itemID INT NOT NULL,\n startParent TEXT,\n startTextNode INT,\n startOffset INT,\n endParent TEXT,\n endTextNode INT,\n endOffset INT,\n dateModified DATE,\n FOREIGN KEY (itemID) REFERENCES itemAttachments(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO highlights SELECT * FROM highlightsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS highlights_itemID");
yield Zotero.DB.queryAsync("CREATE INDEX highlights_itemID ON highlights(itemID)");
yield Zotero.DB.queryAsync("ALTER TABLE customBaseFieldMappings RENAME TO customBaseFieldMappingsOld");
yield Zotero.DB.queryAsync("CREATE TABLE customBaseFieldMappings (\n customItemTypeID INT,\n baseFieldID INT,\n customFieldID INT,\n PRIMARY KEY (customItemTypeID, baseFieldID, customFieldID),\n FOREIGN KEY (customItemTypeID) REFERENCES customItemTypes(customItemTypeID),\n FOREIGN KEY (baseFieldID) REFERENCES fields(fieldID),\n FOREIGN KEY (customFieldID) REFERENCES customFields(customFieldID)\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO customBaseFieldMappings SELECT * FROM customBaseFieldMappingsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS customBaseFieldMappings_baseFieldID");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS customBaseFieldMappings_customFieldID");
yield Zotero.DB.queryAsync("CREATE INDEX customBaseFieldMappings_baseFieldID ON customBaseFieldMappings(baseFieldID)");
yield Zotero.DB.queryAsync("CREATE INDEX customBaseFieldMappings_customFieldID ON customBaseFieldMappings(customFieldID)");
yield Zotero.DB.queryAsync("DELETE FROM settings WHERE setting='account' AND key='libraryID'");
yield Zotero.DB.queryAsync("DROP TABLE annotationsOld");
yield Zotero.DB.queryAsync("DROP TABLE collectionItemsOld");
yield Zotero.DB.queryAsync("DROP TABLE customBaseFieldMappingsOld");
yield Zotero.DB.queryAsync("DROP TABLE deletedItemsOld");
yield Zotero.DB.queryAsync("DROP TABLE fulltextItemWordsOld");
yield Zotero.DB.queryAsync("DROP TABLE fulltextItemsOld");
yield Zotero.DB.queryAsync("DROP TABLE groupItemsOld");
yield Zotero.DB.queryAsync("DROP TABLE groupsOld");
yield Zotero.DB.queryAsync("DROP TABLE highlightsOld");
yield Zotero.DB.queryAsync("DROP TABLE itemAttachmentsOld");
yield Zotero.DB.queryAsync("DROP TABLE itemCreatorsOld");
yield Zotero.DB.queryAsync("DROP TABLE itemDataOld");
yield Zotero.DB.queryAsync("DROP TABLE itemNotesOld");
yield Zotero.DB.queryAsync("DROP TABLE itemSeeAlsoOld");
yield Zotero.DB.queryAsync("DROP TABLE itemTagsOld");
yield Zotero.DB.queryAsync("DROP TABLE relationsOld");
yield Zotero.DB.queryAsync("DROP TABLE savedSearchesOld");
yield Zotero.DB.queryAsync("DROP TABLE storageDeleteLogOld");
yield Zotero.DB.queryAsync("DROP TABLE syncDeleteLogOld");
yield Zotero.DB.queryAsync("DROP TABLE syncedSettingsOld");
yield Zotero.DB.queryAsync("DROP TABLE collectionsOld");
yield Zotero.DB.queryAsync("DROP TABLE creatorsOld");
yield Zotero.DB.queryAsync("DROP TABLE creatorData");
yield Zotero.DB.queryAsync("DROP TABLE itemsOld");
yield Zotero.DB.queryAsync("DROP TABLE tagsOld");
}
Zotero.debug('Updating user data tables from version ' + fromVersion + ' to ' + toVersion);
yield _updateDBVersion('userdata', toVersion);
return Zotero.DB.executeTransaction(function* (conn) {
// Step through version changes until we reach the current version
//
// Each block performs the changes necessary to move from the
// previous revision to that one.
for (let i = fromVersion + 1; i <= toVersion; i++) {
if (i == 80) {
yield _updateDBVersion('compatibility', 1);
yield Zotero.DB.queryAsync("INSERT INTO libraries VALUES (1, 'user')");
yield Zotero.DB.queryAsync("INSERT INTO libraries VALUES (2, 'publications')");
let oldUserLibraryID = yield Zotero.DB.valueQueryAsync("SELECT value FROM settings WHERE setting='account' AND key='libraryID'");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO syncObjectTypes VALUES (7, 'setting')");
yield Zotero.DB.queryAsync("DELETE FROM version WHERE schema IN ('userdata2', 'userdata3')");
yield Zotero.DB.queryAsync("ALTER TABLE libraries ADD COLUMN version INT NOT NULL DEFAULT 0");
yield Zotero.DB.queryAsync("ALTER TABLE libraries ADD COLUMN lastsync INT NOT NULL DEFAULT 0");
yield Zotero.DB.queryAsync("CREATE TABLE syncCache (\n libraryID INT NOT NULL,\n key TEXT NOT NULL,\n syncObjectTypeID INT NOT NULL,\n version INT NOT NULL,\n data TEXT,\n PRIMARY KEY (libraryID, key, syncObjectTypeID),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE,\n FOREIGN KEY (syncObjectTypeID) REFERENCES syncObjectTypes(syncObjectTypeID)\n)");
yield Zotero.DB.queryAsync("DROP TABLE translatorCache");
yield Zotero.DB.queryAsync("CREATE TABLE translatorCache (\n fileName TEXT PRIMARY KEY,\n metadataJSON TEXT,\n lastModifiedTime INT\n);");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_annotations_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_annotations_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_annotations_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemAttachments_itemID_annotations_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_collections_parentCollectionID_collections_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_collections_parentCollectionID_collections_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_collections_parentCollectionID_collections_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_collections_collectionID_collections_parentCollectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_collectionItems_collectionID_collections_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_collectionItems_collectionID_collections_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_collectionItems_collectionID_collections_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_collections_collectionID_collectionItems_collectionID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_collectionItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_collectionItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_collectionItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_collectionItems_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_creators_creatorDataID_creatorData_creatorDataID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_creators_creatorDataID_creatorData_creatorDataID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_creators_creatorDataID_creatorData_creatorDataID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_creatorData_creatorDataID_creators_creatorDataID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_customBaseFieldMappings_customItemTypeID_customItemTypes_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customBaseFieldMappings_customItemTypeID_customItemTypes_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_customBaseFieldMappings_customItemTypeID_customItemTypes_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customItemTypes_customItemTypeID_customBaseFieldMappings_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_customBaseFieldMappings_baseFieldID_fields_fieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customBaseFieldMappings_baseFieldID_fields_fieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_customBaseFieldMappings_customFieldID_customFields_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customBaseFieldMappings_customFieldID_customFields_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_customBaseFieldMappings_customFieldID_customFields_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customFields_customFieldID_customBaseFieldMappings_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_customItemTypeFields_customItemTypeID_customItemTypes_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customItemTypeFields_customItemTypeID_customItemTypes_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_customItemTypeFields_customItemTypeID_customItemTypes_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customItemTypes_customItemTypeID_customItemTypeFields_customItemTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_customItemTypeFields_fieldID_fields_fieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customItemTypeFields_fieldID_fields_fieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customItemTypeFields_customFieldID_customFields_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_customItemTypeFields_customFieldID_customFields_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_customFields_customFieldID_customItemTypeFields_customFieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_fulltextItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_fulltextItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_fulltextItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_fulltextItems_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_fulltextItemWords_wordID_fulltextWords_wordID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_fulltextItemWords_wordID_fulltextWords_wordID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_fulltextItemWords_wordID_fulltextWords_wordID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_fulltextWords_wordID_fulltextItemWords_wordID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_fulltextItemWords_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_fulltextItemWords_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_fulltextItemWords_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_fulltextItemWords_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_groups_libraryID_libraries_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_groups_libraryID_libraries_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_groups_libraryID_libraries_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_libraries_libraryID_groups_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_groupItems_createdByUserID_users_userID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_groupItems_createdByUserID_users_userID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_groupItems_createdByUserID_users_userID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_users_userID_groupItems_createdByUserID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_groupItems_lastModifiedByUserID_users_userID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_groupItems_lastModifiedByUserID_users_userID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_groupItems_lastModifiedByUserID_users_userID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_users_userID_groupItems_lastModifiedByUserID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_highlights_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_highlights_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_highlights_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemAttachments_itemID_highlights_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemAttachments_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemAttachments_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemAttachments_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemAttachments_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemAttachments_sourceItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemAttachments_sourceItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemAttachments_sourceItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemAttachments_sourceItemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemCreators_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemCreators_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemCreators_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemCreators_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemCreators_creatorID_creators_creatorID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemCreators_creatorID_creators_creatorID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemCreators_creatorID_creators_creatorID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_creators_creatorID_itemCreators_creatorID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemCreators_creatorTypeID_creatorTypes_creatorTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemCreators_creatorTypeID_creatorTypes_creatorTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemCreators_creatorTypeID_creatorTypes_creatorTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_creatorTypes_creatorTypeID_itemCreators_creatorTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemCreators_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemCreators_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemData_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemData_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemData_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemData_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemData_fieldID_fields_fieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemData_fieldID_fields_fieldID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemData_valueID_itemDataValues_valueID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemData_valueID_itemDataValues_valueID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemData_valueID_itemDataValues_valueID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemDataValues_valueID_itemData_valueID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemNotes_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemNotes_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemNotes_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemNotes_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemNotes_sourceItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemNotes_sourceItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemNotes_sourceItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemNotes_sourceItemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_items_libraryID_libraries_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_libraryID_libraries_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_items_libraryID_libraries_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_libraries_libraryID_items_libraryID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemSeeAlso_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemSeeAlso_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemSeeAlso_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemSeeAlso_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemSeeAlso_linkedItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemSeeAlso_linkedItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemSeeAlso_linkedItemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_itemSeeAlso_linkedItemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemTags_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemTags_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemTags_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_items_itemID_itemTags_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_itemTags_tagID_tags_tagID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_itemTags_tagID_tags_tagID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_itemTags_tagID_tags_tagID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_tags_tagID_itemTags_tagID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_savedSearchConditions_savedSearchID_savedSearches_savedSearchID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_savedSearchConditions_savedSearchID_savedSearches_savedSearchID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_savedSearchConditions_savedSearchID_savedSearches_savedSearchID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_savedSearches_savedSearchID_savedSearchConditions_savedSearchID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_deletedItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_deletedItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_deletedItems_itemID_items_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_items_itemID_deletedItems_itemID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_syncDeleteLog_syncObjectTypeID_syncObjectTypes_syncObjectTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_syncDeleteLog_syncObjectTypeID_syncObjectTypes_syncObjectTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_syncDeleteLog_syncObjectTypeID_syncObjectTypes_syncObjectTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_syncObjectTypes_syncObjectTypeID_syncDeleteLog_syncObjectTypeID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fki_proxyHosts_proxyID_proxies_proxyID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_proxyHosts_proxyID_proxies_proxyID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fkd_proxyHosts_proxyID_proxies_proxyID");
yield Zotero.DB.queryAsync("DROP TRIGGER IF EXISTS fku_proxies_proxyID_proxyHosts_proxyID");
yield Zotero.DB.queryAsync("ALTER TABLE collections RENAME TO collectionsOld");
yield Zotero.DB.queryAsync("CREATE TABLE collections (\n collectionID INTEGER PRIMARY KEY,\n collectionName TEXT NOT NULL,\n parentCollectionID INT DEFAULT NULL,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n libraryID INT NOT NULL,\n key TEXT NOT NULL,\n version INT NOT NULL DEFAULT 0,\n synced INT NOT NULL DEFAULT 0,\n UNIQUE (libraryID, key),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE,\n FOREIGN KEY (parentCollectionID) REFERENCES collections(collectionID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO collections SELECT collectionID, collectionName, parentCollectionID, clientDateModified, IFNULL(libraryID, 1), key, 0, 0 FROM collectionsOld ORDER BY collectionID DESC");
yield Zotero.DB.queryAsync("CREATE INDEX collections_synced ON collections(synced)");
yield Zotero.DB.queryAsync("ALTER TABLE items RENAME TO itemsOld");
yield Zotero.DB.queryAsync("CREATE TABLE items (\n itemID INTEGER PRIMARY KEY,\n itemTypeID INT NOT NULL,\n dateAdded TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n dateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n libraryID INT NOT NULL,\n key TEXT NOT NULL,\n version INT NOT NULL DEFAULT 0,\n synced INT NOT NULL DEFAULT 0,\n UNIQUE (libraryID, key),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO items SELECT itemID, itemTypeID, dateAdded, dateModified, clientDateModified, IFNULL(libraryID, 1), key, 0, 0 FROM itemsOld ORDER BY dateAdded DESC");
yield Zotero.DB.queryAsync("CREATE INDEX items_synced ON items(synced)");
yield Zotero.DB.queryAsync("ALTER TABLE creators RENAME TO creatorsOld");
yield Zotero.DB.queryAsync("CREATE TABLE creators (\n creatorID INTEGER PRIMARY KEY,\n firstName TEXT,\n lastName TEXT,\n fieldMode INT,\n UNIQUE (lastName, firstName, fieldMode)\n)");
yield Zotero.DB.queryAsync("INSERT INTO creators SELECT creatorDataID, firstName, lastName, fieldMode FROM creatorData");
yield Zotero.DB.queryAsync("ALTER TABLE itemCreators RENAME TO itemCreatorsOld");
yield Zotero.DB.queryAsync("CREATE TABLE itemCreators (\n itemID INT NOT NULL,\n creatorID INT NOT NULL,\n creatorTypeID INT NOT NULL DEFAULT 1,\n orderIndex INT NOT NULL DEFAULT 0,\n PRIMARY KEY (itemID, creatorID, creatorTypeID, orderIndex),\n UNIQUE (itemID, orderIndex),\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (creatorID) REFERENCES creators(creatorID) ON DELETE CASCADE,\n FOREIGN KEY (creatorTypeID) REFERENCES creatorTypes(creatorTypeID)\n)");
yield Zotero.DB.queryAsync("CREATE INDEX itemCreators_creatorTypeID ON itemCreators(creatorTypeID)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO itemCreators SELECT itemID, C.creatorID, creatorTypeID, orderIndex FROM itemCreatorsOld ICO JOIN creatorsOld CO USING (creatorID) JOIN creators C ON (CO.creatorDataID=C.creatorID)");
yield Zotero.DB.queryAsync("ALTER TABLE savedSearches RENAME TO savedSearchesOld");
yield Zotero.DB.queryAsync("CREATE TABLE savedSearches (\n savedSearchID INTEGER PRIMARY KEY,\n savedSearchName TEXT NOT NULL,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n libraryID INT NOT NULL,\n key TEXT NOT NULL,\n version INT NOT NULL DEFAULT 0,\n synced INT NOT NULL DEFAULT 0,\n UNIQUE (libraryID, key),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO savedSearches SELECT savedSearchID, savedSearchName, clientDateModified, IFNULL(libraryID, 1), key, 0, 0 FROM savedSearchesOld ORDER BY savedSearchID DESC");
yield Zotero.DB.queryAsync("CREATE INDEX savedSearches_synced ON savedSearches(synced)");
yield Zotero.DB.queryAsync("ALTER TABLE tags RENAME TO tagsOld");
yield Zotero.DB.queryAsync("CREATE TABLE tags (\n tagID INTEGER PRIMARY KEY,\n libraryID INT NOT NULL,\n name TEXT NOT NULL,\n UNIQUE (libraryID, name)\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO tags SELECT tagID, IFNULL(libraryID, 1), name FROM tagsOld");
yield Zotero.DB.queryAsync("ALTER TABLE itemTags RENAME TO itemTagsOld");
yield Zotero.DB.queryAsync("CREATE TABLE itemTags (\n itemID INT NOT NULL,\n tagID INT NOT NULL,\n type INT NOT NULL,\n PRIMARY KEY (itemID, tagID),\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (tagID) REFERENCES tags(tagID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO itemTags SELECT itemID, T.tagID, TOld.type FROM itemTagsOld ITO JOIN tagsOld TOld USING (tagID) JOIN tags T ON (IFNULL(TOld.libraryID, 1)=T.libraryID AND TOld.name=T.name COLLATE BINARY)");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS itemTags_tagID");
yield Zotero.DB.queryAsync("CREATE INDEX itemTags_tagID ON itemTags(tagID)");
yield Zotero.DB.queryAsync("CREATE TABLE IF NOT EXISTS syncedSettings (\n setting TEXT NOT NULL,\n libraryID INT NOT NULL,\n value NOT NULL,\n version INT NOT NULL DEFAULT 0,\n synced INT NOT NULL DEFAULT 0,\n PRIMARY KEY (setting, libraryID)\n)");
yield Zotero.DB.queryAsync("ALTER TABLE syncedSettings RENAME TO syncedSettingsOld");
yield Zotero.DB.queryAsync("CREATE TABLE syncedSettings (\n setting TEXT NOT NULL,\n libraryID INT NOT NULL,\n value NOT NULL,\n version INT NOT NULL DEFAULT 0,\n synced INT NOT NULL DEFAULT 0,\n PRIMARY KEY (setting, libraryID),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("UPDATE syncedSettingsOld SET libraryID=1 WHERE libraryID=0");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO syncedSettings SELECT * FROM syncedSettingsOld");
yield Zotero.DB.queryAsync("ALTER TABLE itemData RENAME TO itemDataOld");
yield Zotero.DB.queryAsync("CREATE TABLE itemData (\n itemID INT,\n fieldID INT,\n valueID,\n PRIMARY KEY (itemID, fieldID),\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (fieldID) REFERENCES fieldsCombined(fieldID),\n FOREIGN KEY (valueID) REFERENCES itemDataValues(valueID)\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO itemData SELECT * FROM itemDataOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS itemData_fieldID");
yield Zotero.DB.queryAsync("CREATE INDEX itemData_fieldID ON itemData(fieldID)");
yield Zotero.DB.queryAsync("ALTER TABLE itemNotes RENAME TO itemNotesOld");
yield Zotero.DB.queryAsync("CREATE TABLE itemNotes (\n itemID INTEGER PRIMARY KEY,\n parentItemID INT,\n note TEXT,\n title TEXT,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (parentItemID) REFERENCES items(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO itemNotes SELECT * FROM itemNotesOld");
yield Zotero.DB.queryAsync("CREATE INDEX itemNotes_parentItemID ON itemNotes(parentItemID)");
yield Zotero.DB.queryAsync("ALTER TABLE itemAttachments RENAME TO itemAttachmentsOld");
yield Zotero.DB.queryAsync("CREATE TABLE itemAttachments (\n itemID INTEGER PRIMARY KEY,\n parentItemID INT,\n linkMode INT,\n contentType TEXT,\n charsetID INT,\n path TEXT,\n syncState INT DEFAULT 0,\n storageModTime INT,\n storageHash TEXT,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (parentItemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (charsetID) REFERENCES charsets(charsetID) ON DELETE SET NULL\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO itemAttachments SELECT itemID, sourceItemID, linkMode, mimeType, charsetID, path, syncState, storageModTime, storageHash FROM itemAttachmentsOld");
yield Zotero.DB.queryAsync("CREATE INDEX itemAttachments_parentItemID ON itemAttachments(parentItemID)");
yield Zotero.DB.queryAsync("CREATE INDEX itemAttachments_charsetID ON itemAttachments(charsetID)");
yield Zotero.DB.queryAsync("CREATE INDEX itemAttachments_contentType ON itemAttachments(contentType)");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS itemAttachments_syncState");
yield Zotero.DB.queryAsync("CREATE INDEX itemAttachments_syncState ON itemAttachments(syncState)");
yield Zotero.DB.queryAsync("ALTER TABLE itemSeeAlso RENAME TO itemSeeAlsoOld");
yield Zotero.DB.queryAsync("CREATE TABLE itemSeeAlso (\n itemID INT NOT NULL,\n linkedItemID INT NOT NULL,\n PRIMARY KEY (itemID, linkedItemID),\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (linkedItemID) REFERENCES items(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO itemSeeAlso SELECT * FROM itemSeeAlsoOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS itemSeeAlso_linkedItemID");
yield Zotero.DB.queryAsync("CREATE INDEX itemSeeAlso_linkedItemID ON itemSeeAlso(linkedItemID)");
yield Zotero.DB.queryAsync("ALTER TABLE collectionItems RENAME TO collectionItemsOld");
yield Zotero.DB.queryAsync("CREATE TABLE collectionItems (\n collectionID INT NOT NULL,\n itemID INT NOT NULL,\n orderIndex INT NOT NULL DEFAULT 0,\n PRIMARY KEY (collectionID, itemID),\n FOREIGN KEY (collectionID) REFERENCES collections(collectionID) ON DELETE CASCADE,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO collectionItems SELECT * FROM collectionItemsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS itemID"); // incorrect old name
yield Zotero.DB.queryAsync("CREATE INDEX collectionItems_itemID ON collectionItems(itemID)");
yield Zotero.DB.queryAsync("ALTER TABLE savedSearchConditions RENAME TO savedSearchConditionsOld");
yield Zotero.DB.queryAsync("CREATE TABLE savedSearchConditions (\n savedSearchID INT NOT NULL,\n searchConditionID INT NOT NULL,\n condition TEXT NOT NULL,\n operator TEXT,\n value TEXT,\n required NONE,\n PRIMARY KEY (savedSearchID, searchConditionID),\n FOREIGN KEY (savedSearchID) REFERENCES savedSearches(savedSearchID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO savedSearchConditions SELECT * FROM savedSearchConditionsOld");
yield Zotero.DB.queryAsync("DROP TABLE savedSearchConditionsOld");
yield Zotero.DB.queryAsync("ALTER TABLE deletedItems RENAME TO deletedItemsOld");
yield Zotero.DB.queryAsync("CREATE TABLE deletedItems (\n itemID INTEGER PRIMARY KEY,\n dateDeleted DEFAULT CURRENT_TIMESTAMP NOT NULL,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO deletedItems SELECT * FROM deletedItemsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS deletedItems_dateDeleted");
yield Zotero.DB.queryAsync("CREATE INDEX deletedItems_dateDeleted ON deletedItems(dateDeleted)");
yield Zotero.DB.queryAsync("UPDATE relations SET libraryID=1 WHERE libraryID=?", oldUserLibraryID);
yield Zotero.DB.queryAsync("ALTER TABLE relations RENAME TO relationsOld");
yield Zotero.DB.queryAsync("CREATE TABLE relations (\n libraryID INT NOT NULL,\n subject TEXT NOT NULL,\n predicate TEXT NOT NULL,\n object TEXT NOT NULL,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (subject, predicate, object),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO relations SELECT * FROM relationsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS relations_object");
yield Zotero.DB.queryAsync("CREATE INDEX relations_object ON relations(object)");
yield Zotero.DB.queryAsync("ALTER TABLE groups RENAME TO groupsOld");
yield Zotero.DB.queryAsync("CREATE TABLE groups (\n groupID INTEGER PRIMARY KEY,\n libraryID INT NOT NULL UNIQUE,\n name TEXT NOT NULL,\n description TEXT NOT NULL,\n editable INT NOT NULL,\n filesEditable INT NOT NULL,\n version INT NOT NULL,\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO groups SELECT groupID, libraryID, name, description, editable, filesEditable, 0 FROM groupsOld");
yield Zotero.DB.queryAsync("ALTER TABLE groupItems RENAME TO groupItemsOld");
yield Zotero.DB.queryAsync("CREATE TABLE groupItems (\n itemID INTEGER PRIMARY KEY,\n createdByUserID INT,\n lastModifiedByUserID INT,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (createdByUserID) REFERENCES users(userID) ON DELETE SET NULL,\n FOREIGN KEY (lastModifiedByUserID) REFERENCES users(userID) ON DELETE SET NULL\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO groupItems SELECT * FROM groupItemsOld");
let cols = yield Zotero.DB.getColumns('fulltextItems');
if (cols.indexOf("synced") == -1) {
Zotero.DB.queryAsync("ALTER TABLE fulltextItems ADD COLUMN synced INT DEFAULT 0");
Zotero.DB.queryAsync("REPLACE INTO settings (setting, key, value) VALUES ('fulltext', 'downloadAll', 1)");
}
yield Zotero.DB.queryAsync("ALTER TABLE fulltextItems RENAME TO fulltextItemsOld");
yield Zotero.DB.queryAsync("CREATE TABLE fulltextItems (\n itemID INTEGER PRIMARY KEY,\n version INT,\n indexedPages INT,\n totalPages INT,\n indexedChars INT,\n totalChars INT,\n synced INT DEFAULT 0,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO fulltextItems SELECT * FROM fulltextItemsOld");
yield Zotero.DB.queryAsync("ALTER TABLE fulltextItemWords RENAME TO fulltextItemWordsOld");
yield Zotero.DB.queryAsync("CREATE TABLE fulltextItemWords (\n wordID INT,\n itemID INT,\n PRIMARY KEY (wordID, itemID),\n FOREIGN KEY (wordID) REFERENCES fulltextWords(wordID),\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO fulltextItemWords SELECT * FROM fulltextItemWordsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS fulltextItems_version");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS fulltextItemWords_itemID");
yield Zotero.DB.queryAsync("CREATE INDEX fulltextItems_version ON fulltextItems(version)");
yield Zotero.DB.queryAsync("CREATE INDEX fulltextItemWords_itemID ON fulltextItemWords(itemID)");
yield Zotero.DB.queryAsync("UPDATE syncDeleteLog SET libraryID=1 WHERE libraryID=0");
yield Zotero.DB.queryAsync("ALTER TABLE syncDeleteLog RENAME TO syncDeleteLogOld");
yield Zotero.DB.queryAsync("CREATE TABLE syncDeleteLog (\n syncObjectTypeID INT NOT NULL,\n libraryID INT NOT NULL,\n key TEXT NOT NULL,\n synced INT NOT NULL DEFAULT 0,\n UNIQUE (syncObjectTypeID, libraryID, key),\n FOREIGN KEY (syncObjectTypeID) REFERENCES syncObjectTypes(syncObjectTypeID),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO syncDeleteLog SELECT * FROM syncDeleteLogOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS syncDeleteLog_timestamp");
yield Zotero.DB.queryAsync("CREATE INDEX syncDeleteLog_synced ON syncDeleteLog(synced)");
yield Zotero.DB.queryAsync("ALTER TABLE storageDeleteLog RENAME TO storageDeleteLogOld");
yield Zotero.DB.queryAsync("CREATE TABLE storageDeleteLog (\n libraryID INT NOT NULL,\n key TEXT NOT NULL,\n synced INT NOT NULL DEFAULT 0,\n PRIMARY KEY (libraryID, key),\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO storageDeleteLog SELECT * FROM storageDeleteLogOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS storageDeleteLog_timestamp");
yield Zotero.DB.queryAsync("CREATE INDEX storageDeleteLog_synced ON storageDeleteLog(synced)");
yield Zotero.DB.queryAsync("ALTER TABLE annotations RENAME TO annotationsOld");
yield Zotero.DB.queryAsync("CREATE TABLE annotations (\n annotationID INTEGER PRIMARY KEY,\n itemID INT NOT NULL,\n parent TEXT,\n textNode INT,\n offset INT,\n x INT,\n y INT,\n cols INT,\n rows INT,\n text TEXT,\n collapsed BOOL,\n dateModified DATE,\n FOREIGN KEY (itemID) REFERENCES itemAttachments(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO annotations SELECT * FROM annotationsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS annotations_itemID");
yield Zotero.DB.queryAsync("CREATE INDEX annotations_itemID ON annotations(itemID)");
yield Zotero.DB.queryAsync("ALTER TABLE highlights RENAME TO highlightsOld");
yield Zotero.DB.queryAsync("CREATE TABLE highlights (\n highlightID INTEGER PRIMARY KEY,\n itemID INT NOT NULL,\n startParent TEXT,\n startTextNode INT,\n startOffset INT,\n endParent TEXT,\n endTextNode INT,\n endOffset INT,\n dateModified DATE,\n FOREIGN KEY (itemID) REFERENCES itemAttachments(itemID) ON DELETE CASCADE\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO highlights SELECT * FROM highlightsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS highlights_itemID");
yield Zotero.DB.queryAsync("CREATE INDEX highlights_itemID ON highlights(itemID)");
yield Zotero.DB.queryAsync("ALTER TABLE customBaseFieldMappings RENAME TO customBaseFieldMappingsOld");
yield Zotero.DB.queryAsync("CREATE TABLE customBaseFieldMappings (\n customItemTypeID INT,\n baseFieldID INT,\n customFieldID INT,\n PRIMARY KEY (customItemTypeID, baseFieldID, customFieldID),\n FOREIGN KEY (customItemTypeID) REFERENCES customItemTypes(customItemTypeID),\n FOREIGN KEY (baseFieldID) REFERENCES fields(fieldID),\n FOREIGN KEY (customFieldID) REFERENCES customFields(customFieldID)\n)");
yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO customBaseFieldMappings SELECT * FROM customBaseFieldMappingsOld");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS customBaseFieldMappings_baseFieldID");
yield Zotero.DB.queryAsync("DROP INDEX IF EXISTS customBaseFieldMappings_customFieldID");
yield Zotero.DB.queryAsync("CREATE INDEX customBaseFieldMappings_baseFieldID ON customBaseFieldMappings(baseFieldID)");
yield Zotero.DB.queryAsync("CREATE INDEX customBaseFieldMappings_customFieldID ON customBaseFieldMappings(customFieldID)");
yield Zotero.DB.queryAsync("DELETE FROM settings WHERE setting='account' AND key='libraryID'");
yield Zotero.DB.queryAsync("DROP TABLE annotationsOld");
yield Zotero.DB.queryAsync("DROP TABLE collectionItemsOld");
yield Zotero.DB.queryAsync("DROP TABLE customBaseFieldMappingsOld");
yield Zotero.DB.queryAsync("DROP TABLE deletedItemsOld");
yield Zotero.DB.queryAsync("DROP TABLE fulltextItemWordsOld");
yield Zotero.DB.queryAsync("DROP TABLE fulltextItemsOld");
yield Zotero.DB.queryAsync("DROP TABLE groupItemsOld");
yield Zotero.DB.queryAsync("DROP TABLE groupsOld");
yield Zotero.DB.queryAsync("DROP TABLE highlightsOld");
yield Zotero.DB.queryAsync("DROP TABLE itemAttachmentsOld");
yield Zotero.DB.queryAsync("DROP TABLE itemCreatorsOld");
yield Zotero.DB.queryAsync("DROP TABLE itemDataOld");
yield Zotero.DB.queryAsync("DROP TABLE itemNotesOld");
yield Zotero.DB.queryAsync("DROP TABLE itemSeeAlsoOld");
yield Zotero.DB.queryAsync("DROP TABLE itemTagsOld");
yield Zotero.DB.queryAsync("DROP TABLE relationsOld");
yield Zotero.DB.queryAsync("DROP TABLE savedSearchesOld");
yield Zotero.DB.queryAsync("DROP TABLE storageDeleteLogOld");
yield Zotero.DB.queryAsync("DROP TABLE syncDeleteLogOld");
yield Zotero.DB.queryAsync("DROP TABLE syncedSettingsOld");
yield Zotero.DB.queryAsync("DROP TABLE collectionsOld");
yield Zotero.DB.queryAsync("DROP TABLE creatorsOld");
yield Zotero.DB.queryAsync("DROP TABLE creatorData");
yield Zotero.DB.queryAsync("DROP TABLE itemsOld");
yield Zotero.DB.queryAsync("DROP TABLE tagsOld");
}
}
yield _updateDBVersion('userdata', toVersion);
})
.return(true);
})
}
return true;
}
});
}

View file

@ -947,7 +947,7 @@ Zotero.Search.idsToTempTable = function (ids) {
yield Zotero.DB.queryAsync(sql);
return tmpTable;
}, { exclusive: true });
});
}

View file

@ -740,8 +740,6 @@ var ZoteroPane = new function()
*/
this.newItem = Zotero.Promise.coroutine(function* (typeID, data, row, manual)
{
yield Zotero.DB.waitForTransaction();
if ((row === undefined || row === null) && this.collectionsView.selection) {
row = this.collectionsView.selection.currentIndex;
@ -810,8 +808,6 @@ var ZoteroPane = new function()
this.newCollection = Zotero.Promise.coroutine(function* (parentKey) {
yield Zotero.DB.waitForTransaction();
if (!this.canEditLibrary()) {
this.displayCannotEditLibraryMessage();
return;
@ -847,7 +843,7 @@ var ZoteroPane = new function()
collection.libraryID = libraryID;
collection.name = newName.value;
collection.parentKey = parentKey;
return collection.save();
return collection.saveTx();
});
@ -1234,6 +1230,15 @@ var ZoteroPane = new function()
return Zotero.spawn(function* () {
yield Zotero.DB.waitForTransaction();
// Don't select item until items list has loaded
//
// This avoids an error if New Item is used while the pane is first loading.
var deferred = Zotero.Promise.defer();
this.itemsView.addEventListener('load', function () {
deferred.resolve();
});
yield deferred.promise;
var selectedItems = this.itemsView.getSelectedItems();
// Check if selection has actually changed. The onselect event that calls this
@ -1863,7 +1868,7 @@ var ZoteroPane = new function()
if (result && newName.value) {
row.ref.name = newName.value;
row.ref.save();
row.ref.saveTx();
}
}
else {
@ -3055,8 +3060,6 @@ var ZoteroPane = new function()
* @return {Promise}
*/
this.newNote = Zotero.Promise.coroutine(function* (popup, parentKey, text, citeURI) {
yield Zotero.DB.waitForTransaction();
if (!this.canEdit()) {
this.displayCannotEditLibraryMessage();
return;
@ -3081,7 +3084,7 @@ var ZoteroPane = new function()
if (parentKey) {
item.parentKey = parentKey;
}
var itemID = yield item.save();
var itemID = yield item.saveTx();
if (!parentKey && this.itemsView && this.collectionsView.selectedTreeRow.isCollection()) {
yield this.collectionsView.selectedTreeRow.ref.addItem(itemID);
@ -3146,7 +3149,7 @@ var ZoteroPane = new function()
var note = items[0].getNote()
items[0].setNote(note + text);
yield items[0].save();
yield items[0].saveTx();
var noteElem = document.getElementById('zotero-note-editor')
noteElem.focus();
@ -3537,7 +3540,7 @@ var ZoteroPane = new function()
item.setField('title', attachmentItem.getField('title'));
item.setField('url', attachmentItem.getField('url'));
item.setField('accessDate', attachmentItem.getField('accessDate'));
yield item.save();
yield item.saveTx();
}
}
}
@ -4106,7 +4109,7 @@ var ZoteroPane = new function()
}
item.setField('title', newName);
yield item.save();
yield item.saveTx();
}
return true;

View file

@ -24,7 +24,7 @@ describe("Zotero.Attachments", function() {
// Create parent item
var item = new Zotero.Item('book');
var parentItemID = yield item.save();
var parentItemID = yield item.saveTx();
// Create attachment and compare content
var itemID = yield Zotero.Attachments.importFromFile(tmpFile, parentItemID);
@ -43,7 +43,7 @@ describe("Zotero.Attachments", function() {
// Create parent item
var item = new Zotero.Item('book');
var parentItemID = yield item.save();
var parentItemID = yield item.saveTx();
// Create attachment and compare content
var itemID = yield Zotero.Attachments.importFromFile(file, parentItemID);

View file

@ -6,7 +6,7 @@ describe("Zotero.Collection", function() {
var name = "Test";
var collection = new Zotero.Collection;
collection.name = name;
var id = yield collection.save();
var id = yield collection.saveTx();
collection = yield Zotero.Collections.getAsync(id);
assert.equal(collection.name, name);
});
@ -18,7 +18,7 @@ describe("Zotero.Collection", function() {
var collection = new Zotero.Collection
collection.version = version;
collection.name = "Test";
var id = yield collection.save();
var id = yield collection.saveTx();
collection = yield Zotero.Collections.getAsync(id);
assert.equal(collection.version, version);
});
@ -28,13 +28,13 @@ describe("Zotero.Collection", function() {
it("should set parent collection for new collections", function* () {
var parentCol = new Zotero.Collection
parentCol.name = "Parent";
var parentID = yield parentCol.save();
var parentID = yield parentCol.saveTx();
var {libraryID, key: parentKey} = Zotero.Collections.getLibraryAndKeyFromID(parentID);
var col = new Zotero.Collection
col.name = "Child";
col.parentKey = parentKey;
var id = yield col.save();
var id = yield col.saveTx();
col = yield Zotero.Collections.getAsync(id);
assert.equal(col.parentKey, parentKey);
});
@ -43,25 +43,25 @@ describe("Zotero.Collection", function() {
// Create initial parent collection
var parentCol = new Zotero.Collection
parentCol.name = "Parent";
var parentID = yield parentCol.save();
var parentID = yield parentCol.saveTx();
var {libraryID, key: parentKey} = Zotero.Collections.getLibraryAndKeyFromID(parentID);
// Create subcollection
var col = new Zotero.Collection
col.name = "Child";
col.parentKey = parentKey;
var id = yield col.save();
var id = yield col.saveTx();
col = yield Zotero.Collections.getAsync(id);
// Create new parent collection
var newParentCol = new Zotero.Collection
newParentCol.name = "New Parent";
var newParentID = yield newParentCol.save();
var newParentID = yield newParentCol.saveTx();
var {libraryID, key: newParentKey} = Zotero.Collections.getLibraryAndKeyFromID(newParentID);
// Change parent collection
col.parentKey = newParentKey;
yield col.save();
yield col.saveTx();
col = yield Zotero.Collections.getAsync(id);
assert.equal(col.parentKey, newParentKey);
});
@ -70,14 +70,14 @@ describe("Zotero.Collection", function() {
// Create initial parent collection
var parentCol = new Zotero.Collection
parentCol.name = "Parent";
var parentID = yield parentCol.save();
var parentID = yield parentCol.saveTx();
var {libraryID, key: parentKey} = Zotero.Collections.getLibraryAndKeyFromID(parentID);
// Create subcollection
var col = new Zotero.Collection
col.name = "Child";
col.parentKey = parentKey;
var id = yield col.save();
var id = yield col.saveTx();
col = yield Zotero.Collections.getAsync(id);
// Set to existing parent
@ -88,11 +88,11 @@ describe("Zotero.Collection", function() {
it("should not resave a collection with no parent if set to false", function* () {
var col = new Zotero.Collection
col.name = "Test";
var id = yield col.save();
var id = yield col.saveTx();
col = yield Zotero.Collections.getAsync(id);
col.parentKey = false;
var ret = yield col.save();
var ret = yield col.saveTx();
assert.isFalse(ret);
});
})

View file

@ -26,7 +26,7 @@ describe("Zotero.CollectionTreeView", function() {
// Create collection
var collection = new Zotero.Collection;
collection.name = "Select new collection";
var id = yield collection.save();
var id = yield collection.saveTx();
// New collection should be selected
var selected = collectionsView.getSelectedCollection(true);
@ -39,7 +39,7 @@ describe("Zotero.CollectionTreeView", function() {
// Create collection with skipNotifier flag
var collection = new Zotero.Collection;
collection.name = "No select on skipNotifier";
var id = yield collection.save({
var id = yield collection.saveTx({
skipNotifier: true
});
@ -53,7 +53,7 @@ describe("Zotero.CollectionTreeView", function() {
// Create collection with skipSelect flag
var collection = new Zotero.Collection;
collection.name = "No select on skipSelect";
var id = yield collection.save({
var id = yield collection.saveTx({
skipSelect: true
});
@ -65,13 +65,13 @@ describe("Zotero.CollectionTreeView", function() {
// Create collection
var collection = new Zotero.Collection;
collection.name = "No select on modify";
var id = yield collection.save();
var id = yield collection.saveTx();
collection = yield Zotero.Collections.getAsync(id);
resetSelection();
collection.name = "No select on modify 2";
yield collection.save();
yield collection.saveTx();
// Modified collection should not be selected
assert.equal(collectionsView.getSelectedLibraryID(), Zotero.Libraries.userLibraryID);
@ -81,14 +81,14 @@ describe("Zotero.CollectionTreeView", function() {
// Create collection
var collection = new Zotero.Collection;
collection.name = "Reselect on modify";
var id = yield collection.save();
var id = yield collection.saveTx();
collection = yield Zotero.Collections.getAsync(id);
var selected = collectionsView.getSelectedCollection(true);
assert.equal(selected, id);
collection.name = "Reselect on modify 2";
yield collection.save();
yield collection.saveTx();
// Modified collection should still be selected
selected = collectionsView.getSelectedCollection(true);
@ -98,13 +98,13 @@ describe("Zotero.CollectionTreeView", function() {
it("should add a saved search after collections", function* () {
var collection = new Zotero.Collection;
collection.name = "Test";
var collectionID = yield collection.save();
var collectionID = yield collection.saveTx();
var cv = win.ZoteroPane.collectionsView;
var search = new Zotero.Search;
search.name = "A Test Search";
search.addCondition('title', 'contains', 'test');
var searchID = yield search.save();
var searchID = yield search.saveTx();
var collectionRow = cv._rowMap["C" + collectionID];
var searchRow = cv._rowMap["S" + searchID];

View file

@ -6,7 +6,7 @@ describe("Zotero.DataObject", function() {
describe("#loadAllData()", function () {
it("should load data on a regular item", function* () {
var item = new Zotero.Item('book');
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
yield item.loadAllData();
assert.throws(item.getNote.bind(item), 'getNote() can only be called on notes and attachments');
@ -14,7 +14,7 @@ describe("Zotero.DataObject", function() {
it("should load data on an attachment item", function* () {
var item = new Zotero.Item('attachment');
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
yield item.loadAllData();
assert.equal(item.getNote(), '');
@ -22,7 +22,7 @@ describe("Zotero.DataObject", function() {
it("should load data on a note item", function* () {
var item = new Zotero.Item('note');
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
yield item.loadAllData();
assert.equal(item.getNote(), '');
@ -35,7 +35,7 @@ describe("Zotero.DataObject", function() {
var objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType('collection');
var obj = new Zotero.Collection;
obj.name = "Test";
var id = yield obj.save();
var id = yield obj.saveTx();
var { libraryID, key } = objectsClass.getLibraryAndKeyFromID(id);
assert.typeOf(key, 'string');
assert.equal(objectsClass.getIDFromLibraryAndKey(libraryID, key), id);
@ -44,7 +44,7 @@ describe("Zotero.DataObject", function() {
var objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType('search');
var obj = new Zotero.Search;
obj.name = "Test";
var id = yield obj.save();
var id = yield obj.saveTx();
var { libraryID, key } = objectsClass.getLibraryAndKeyFromID(id);
assert.typeOf(key, 'string');
assert.equal(objectsClass.getIDFromLibraryAndKey(libraryID, key), id);
@ -52,7 +52,7 @@ describe("Zotero.DataObject", function() {
// Item
var objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType('item');
var obj = new Zotero.Item('book');
var id = yield obj.save();
var id = yield obj.saveTx();
var { libraryID, key } = objectsClass.getLibraryAndKeyFromID(id);
assert.typeOf(key, 'string');
assert.equal(objectsClass.getIDFromLibraryAndKey(libraryID, key), id);

View file

@ -9,14 +9,15 @@ describe("Zotero.DB", function() {
});
beforeEach(function* () {
yield Zotero.DB.queryAsync("DROP TABLE IF EXISTS " + tmpTable);
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
});
after(function* () {
yield Zotero.DB.queryAsync("DROP TABLE IF EXISTS " + tmpTable);
});
describe("#executeTransaction()", function () {
it("should nest concurrent transactions", Zotero.Promise.coroutine(function* () {
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
it("should serialize concurrent transactions", Zotero.Promise.coroutine(function* () {
this.timeout(1000);
var resolve1, resolve2, reject1, reject2;
var promise1 = new Promise(function (resolve, reject) {
@ -29,18 +30,21 @@ describe("Zotero.DB", function() {
});
Zotero.DB.executeTransaction(function* () {
yield Zotero.Promise.delay(100);
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
Zotero.DB.transactionDate; // Make sure we're still in a transaction
yield Zotero.Promise.delay(250);
var num = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
assert.equal(num, 0);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
assert.ok(Zotero.DB.inTransaction());
})
.then(resolve1)
.catch(reject1);
Zotero.DB.executeTransaction(function* () {
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 1);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
Zotero.DB.transactionDate; // Make sure we're still in a transaction
var num = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
assert.equal(num, 1);
yield Zotero.Promise.delay(500);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
assert.ok(Zotero.DB.inTransaction());
})
.then(resolve2)
.catch(reject2);
@ -48,10 +52,8 @@ describe("Zotero.DB", function() {
yield Zotero.Promise.all([promise1, promise2]);
}));
it("shouldn't nest transactions if an exclusive transaction is open", Zotero.Promise.coroutine(function* () {
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
var resolve1, resolve2, reject1, reject2;
it("should serialize queued transactions", function* () {
var resolve1, resolve2, reject1, reject2, resolve3, reject3;
var promise1 = new Promise(function (resolve, reject) {
resolve1 = resolve;
reject1 = reject;
@ -60,66 +62,48 @@ describe("Zotero.DB", function() {
resolve2 = resolve;
reject2 = reject;
});
var promise3 = new Promise(function (resolve, reject) {
resolve3 = resolve;
reject3 = reject;
});
// Start a transaction and have it delay
Zotero.DB.executeTransaction(function* () {
yield Zotero.Promise.delay(100);
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
var num = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
assert.equal(num, 0);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
Zotero.DB.transactionDate; // Make sure we're still in a transaction
}, { exclusive: true })
assert.ok(Zotero.DB.inTransaction());
})
.then(resolve1)
.catch(reject1);
// Start two more transactions, which should wait on the first
Zotero.DB.executeTransaction(function* () {
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
var num = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
assert.equal(num, 1);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
Zotero.DB.transactionDate; // Make sure we're still in a transaction
assert.ok(Zotero.DB.inTransaction());
})
.then(resolve2)
.catch(reject2);
yield Zotero.Promise.all([promise1, promise2]);
}));
it("shouldn't nest an exclusive transaction if another transaction is open", Zotero.Promise.coroutine(function* () {
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
var resolve1, resolve2, reject1, reject2;
var promise1 = new Promise(function (resolve, reject) {
resolve1 = resolve;
reject1 = reject;
});
var promise2 = new Promise(function (resolve, reject) {
resolve2 = resolve;
reject2 = reject;
});
Zotero.DB.executeTransaction(function* () {
yield Zotero.Promise.delay(100);
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
Zotero.DB.transactionDate; // Make sure we're still in a transaction
})
.then(resolve1)
.catch(reject1);
Zotero.DB.executeTransaction(function* () {
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
var num = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
assert.equal(num, 1);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
Zotero.DB.transactionDate; // Make sure we're still in a transaction
}, { exclusive: true })
.then(resolve2)
.catch(reject2);
assert.equal(num, 2);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (3)");
// But make sure the second queued transaction doesn't start at the same time,
// such that the first queued transaction gets closed while the second is still
// running
assert.ok(Zotero.DB.inTransaction());
})
.then(resolve3)
.catch(reject3);
yield Zotero.Promise.all([promise1, promise2]);
}));
yield Zotero.Promise.all([promise1, promise2, promise3]);
})
it("should roll back on error", function* () {
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
try {
yield Zotero.DB.executeTransaction(function* () {
@ -141,7 +125,6 @@ describe("Zotero.DB", function() {
it("should run onRollback callbacks", function* () {
var callbackRan = false;
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
try {
yield Zotero.DB.executeTransaction(
function* () {
@ -163,25 +146,31 @@ describe("Zotero.DB", function() {
yield Zotero.DB.queryAsync("DROP TABLE " + tmpTable);
});
it("should run onRollback callbacks for nested transactions", function* () {
it("should time out on nested transactions", function* () {
var e;
yield Zotero.DB.executeTransaction(function* () {
e = yield getPromiseError(
Zotero.DB.executeTransaction(function* () {}).timeout(250)
);
});
assert.ok(e);
assert.equal(e.name, "TimeoutError");
});
it("should run onRollback callbacks for timed-out nested transactions", function* () {
var callback1Ran = false;
var callback2Ran = false;
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
try {
yield Zotero.DB.executeTransaction(function* () {
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
yield Zotero.DB.executeTransaction(
function* () {
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
throw 'Aborting transaction -- ignore';
},
function* () {},
{
waitTimeout: 100,
onRollback: function () {
callback1Ran = true;
}
}
);
)
},
{
onRollback: function () {
@ -190,32 +179,10 @@ describe("Zotero.DB", function() {
});
}
catch (e) {
if (typeof e != 'string' || !e.startsWith('Aborting transaction')) throw e;
if (e.name != "TimeoutError") throw e;
}
assert.ok(callback1Ran);
assert.ok(callback2Ran);
yield Zotero.DB.queryAsync("DROP TABLE " + tmpTable);
});
it("should not commit nested transactions", function* () {
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
try {
yield Zotero.DB.executeTransaction(function* () {
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
yield Zotero.DB.executeTransaction(function* () {
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
throw 'Aborting transaction -- ignore';
});
});
}
catch (e) {
if (typeof e != 'string' || !e.startsWith('Aborting transaction')) throw e;
}
var count = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
assert.equal(count, 0);
yield Zotero.DB.queryAsync("DROP TABLE " + tmpTable);
});
})
});

View file

@ -36,7 +36,7 @@ describe("Zotero.Item", function () {
var fieldID = Zotero.ItemFields.getID(field);
var item = new Zotero.Item('book');
item.setField(field, 'Foo');
id = yield item.save();
id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
item.setField(field, "");
@ -57,7 +57,7 @@ describe("Zotero.Item", function () {
assert.ok(item._changed.itemData[fieldID]);
assert.ok(item.hasChanged());
yield item.save();
yield item.saveTx();
assert.isFalse(item.getField(fieldID));
})
@ -70,7 +70,7 @@ describe("Zotero.Item", function () {
it("should save version as object version", function* () {
var item = new Zotero.Item('book');
item.setField("version", 1);
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
assert.equal(item.getField("version"), 1);
});
@ -78,7 +78,7 @@ describe("Zotero.Item", function () {
it("should save versionNumber for computerProgram", function () {
var item = new Zotero.Item('computerProgram');
item.setField("versionNumber", "1.0");
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
assert.equal(item.getField("versionNumber"), "1.0");
});
@ -89,7 +89,7 @@ describe("Zotero.Item", function () {
var dateModified = "2015-05-05 17:18:12";
var item = new Zotero.Item('book');
item.dateModified = dateModified;
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
assert.equal(item.dateModified, dateModified);
})
@ -98,7 +98,7 @@ describe("Zotero.Item", function () {
var dateModified = "2015-05-05 17:18:12";
var item = new Zotero.Item('book');
item.dateModified = dateModified;
var id = yield item.save({
var id = yield item.saveTx({
skipDateModifiedUpdate: true
});
item = yield Zotero.Items.getAsync(id);
@ -109,13 +109,13 @@ describe("Zotero.Item", function () {
var dateModified = "2015-05-05 17:18:12";
var item = new Zotero.Item('book');
item.dateModified = dateModified;
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
// Save again without changing Date Modified
yield item.loadItemData();
item.setField('title', 'Test');
yield item.save()
yield item.saveTx()
assert.closeTo(Zotero.Date.sqlToDate(item.dateModified, true).getTime(), Date.now(), 1000);
})
@ -124,20 +124,20 @@ describe("Zotero.Item", function () {
var dateModified = "2015-05-05 17:18:12";
var item = new Zotero.Item('book');
item.dateModified = dateModified;
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
// Set Date Modified to existing value
yield item.loadItemData();
item.setField('title', 'Test');
item.dateModified = dateModified;
yield item.save()
yield item.saveTx()
assert.closeTo(Zotero.Date.sqlToDate(item.dateModified, true).getTime(), Date.now(), 1000);
})
it("should use current time if Date Modified is not given when skipDateModifiedUpdate is set for a new item", function* () {
var item = new Zotero.Item('book');
var id = yield item.save({
var id = yield item.saveTx({
skipDateModifiedUpdate: true
});
item = yield Zotero.Items.getAsync(id);
@ -148,13 +148,13 @@ describe("Zotero.Item", function () {
var dateModified = "2015-05-05 17:18:12";
var item = new Zotero.Item('book');
item.dateModified = dateModified;
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
// Resave with skipDateModifiedUpdate
yield item.loadItemData();
item.setField('title', 'Test');
yield item.save({
yield item.saveTx({
skipDateModifiedUpdate: true
})
assert.equal(item.dateModified, dateModified);
@ -164,11 +164,11 @@ describe("Zotero.Item", function () {
describe("#parentID", function () {
it("should create a child note", function* () {
var item = new Zotero.Item('book');
var parentItemID = yield item.save();
var parentItemID = yield item.saveTx();
item = new Zotero.Item('note');
item.parentID = parentItemID;
var childItemID = yield item.save();
var childItemID = yield item.saveTx();
item = yield Zotero.Items.getAsync(childItemID);
assert.ok(item.parentID);
@ -198,7 +198,7 @@ describe("Zotero.Item", function () {
var item = new Zotero.Item('attachment');
item.attachmentLinkMode = 'linked_url';
item.url = "https://www.zotero.org/";
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
item.parentKey = false;
@ -207,15 +207,15 @@ describe("Zotero.Item", function () {
it("should move a top-level note under another item", function* () {
var noteItem = new Zotero.Item('note');
var id = yield noteItem.save()
var id = yield noteItem.saveTx()
noteItem = yield Zotero.Items.getAsync(id);
var item = new Zotero.Item('book');
id = yield item.save();
id = yield item.saveTx();
var { libraryID, key } = Zotero.Items.getLibraryAndKeyFromID(id);
noteItem.parentKey = key;
yield noteItem.save();
yield noteItem.saveTx();
assert.isFalse(noteItem.isTopLevelItem());
})
@ -224,19 +224,19 @@ describe("Zotero.Item", function () {
// Create a collection
var collection = new Zotero.Collection;
collection.name = "Test";
var collectionID = yield collection.save();
var collectionID = yield collection.saveTx();
// Create a top-level note and add it to a collection
var noteItem = new Zotero.Item('note');
noteItem.addToCollection(collectionID);
var id = yield noteItem.save()
var id = yield noteItem.saveTx()
noteItem = yield Zotero.Items.getAsync(id);
var item = new Zotero.Item('book');
id = yield item.save();
id = yield item.saveTx();
var { libraryID, key } = Zotero.Items.getLibraryAndKeyFromID(id);
noteItem.parentKey = key;
yield noteItem.save();
yield noteItem.saveTx();
assert.isFalse(noteItem.isTopLevelItem());
})
@ -258,7 +258,7 @@ describe("Zotero.Item", function () {
var item = new Zotero.Item("journalArticle");
item.setCreators(creators);
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
yield item.loadCreators();
assert.sameDeepMembers(item.getCreatorsJSON(), creators);
@ -282,7 +282,7 @@ describe("Zotero.Item", function () {
var item = new Zotero.Item("journalArticle");
item.setCreators(creators);
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
yield item.loadCreators();
assert.sameDeepMembers(item.getCreators(), creators);
@ -295,7 +295,7 @@ describe("Zotero.Item", function () {
var item = new Zotero.Item("attachment");
item.attachmentLinkMode = Zotero.Attachments.LINK_MODE_IMPORTED_FILE;
item.attachmentCharset = charset;
var itemID = yield item.save();
var itemID = yield item.saveTx();
item = yield Zotero.Items.getAsync(itemID);
assert.equal(item.attachmentCharset, charset);
})
@ -305,7 +305,7 @@ describe("Zotero.Item", function () {
var item = new Zotero.Item("attachment");
item.attachmentLinkMode = Zotero.Attachments.LINK_MODE_IMPORTED_FILE;
item.attachmentCharset = charset;
var itemID = yield item.save();
var itemID = yield item.saveTx();
item = yield Zotero.Items.getAsync(itemID);
// Set charset to same value
@ -320,20 +320,20 @@ describe("Zotero.Item", function () {
// Create parent item
var item = new Zotero.Item("book");
var parentItemID = yield item.save();
var parentItemID = yield item.saveTx();
// Create attachment item
var item = new Zotero.Item("attachment");
item.attachmentLinkMode = Zotero.Attachments.LINK_MODE_IMPORTED_FILE;
item.parentID = parentItemID;
var itemID = yield item.save();
var itemID = yield item.saveTx();
// Should be empty when unset
assert.equal(item.attachmentFilename, '');
// Set filename
item.attachmentFilename = filename;
yield item.save();
yield item.saveTx();
item = yield Zotero.Items.getAsync(itemID);
// Check filename
@ -358,7 +358,7 @@ describe("Zotero.Item", function () {
];
var item = new Zotero.Item('journalArticle');
item.setTags(tags);
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
yield item.loadTags();
assert.sameDeepMembers(item.getTags(tags), tags);
@ -375,7 +375,7 @@ describe("Zotero.Item", function () {
];
var item = new Zotero.Item('journalArticle');
item.setTags(tags);
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
yield item.loadTags();
item.setTags(tags);
@ -393,11 +393,11 @@ describe("Zotero.Item", function () {
];
var item = new Zotero.Item('journalArticle');
item.setTags(tags);
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
yield item.loadTags();
item.setTags(tags.slice(0));
yield item.save();
yield item.saveTx();
assert.sameDeepMembers(item.getTags(tags), tags.slice(0));
})
})

View file

@ -7,7 +7,7 @@ describe("Zotero.ItemTreeView", function() {
itemsView = win.ZoteroPane.itemsView;
var item = new Zotero.Item('book');
existingItemID = yield item.save();
existingItemID = yield item.saveTx();
});
after(function () {
win.close();
@ -47,7 +47,7 @@ describe("Zotero.ItemTreeView", function() {
// Create item
var item = new Zotero.Item('book');
var id = yield item.save();
var id = yield item.saveTx();
// New item should be selected
var selected = itemsView.getSelectedItems();
@ -71,7 +71,7 @@ describe("Zotero.ItemTreeView", function() {
// Create item with skipNotifier flag
var item = new Zotero.Item('book');
var id = yield item.save({
var id = yield item.saveTx({
skipNotifier: true
});
@ -96,7 +96,7 @@ describe("Zotero.ItemTreeView", function() {
// Create item with skipSelect flag
var item = new Zotero.Item('book');
var id = yield item.save({
var id = yield item.saveTx({
skipSelect: true
});
@ -114,7 +114,7 @@ describe("Zotero.ItemTreeView", function() {
it("shouldn't select a modified item", function* () {
// Create item
var item = new Zotero.Item('book');
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
itemsView.selection.clearSelection();
@ -124,7 +124,7 @@ describe("Zotero.ItemTreeView", function() {
// Modify item
item.setField('title', 'no select on modify');
yield item.save();
yield item.saveTx();
// itemSelected should have been called once (from 'selectEventsSuppressed = false'
// in notify()) as a no-op
@ -138,7 +138,7 @@ describe("Zotero.ItemTreeView", function() {
it("should maintain selection on a selected modified item", function* () {
// Create item
var item = new Zotero.Item('book');
var id = yield item.save();
var id = yield item.saveTx();
item = yield Zotero.Items.getAsync(id);
yield itemsView.selectItem(id);
@ -151,7 +151,7 @@ describe("Zotero.ItemTreeView", function() {
// Modify item
item.setField('title', 'maintain selection on modify');
yield item.save();
yield item.saveTx();
// itemSelected should have been called once (from 'selectEventsSuppressed = false'
// in notify()) as a no-op

View file

@ -3,7 +3,7 @@ describe("Zotero.Search", function() {
it("should fail without a name", function* () {
var s = new Zotero.Search;
s.addCondition('title', 'is', 'test');
var e = yield getPromiseError(s.save());
var e = yield getPromiseError(s.saveTx());
assert.ok(e);
assert.equal(e.constructor.name, Error.prototype.constructor.name); // TEMP: Error mismatch
assert.equal(e.message, "Name not provided for saved search");
@ -14,7 +14,7 @@ describe("Zotero.Search", function() {
var s = new Zotero.Search;
s.name = "Test";
s.addCondition('title', 'is', 'test');
var id = yield s.save();
var id = yield s.saveTx();
assert.typeOf(id, 'number');
// Check saved search
@ -40,14 +40,14 @@ describe("Zotero.Search", function() {
s.libraryID = Zotero.Libraries.userLibraryID;
s.name = "Test";
s.addCondition('title', 'is', 'test');
var id = yield s.save();
var id = yield s.saveTx();
assert.typeOf(id, 'number');
// Add condition
s = yield Zotero.Searches.getAsync(id);
yield s.loadConditions();
s.addCondition('title', 'contains', 'foo');
var saved = yield s.save();
var saved = yield s.saveTx();
assert.isTrue(saved);
// Check saved search
@ -64,14 +64,14 @@ describe("Zotero.Search", function() {
s.name = "Test";
s.addCondition('title', 'is', 'test');
s.addCondition('title', 'contains', 'foo');
var id = yield s.save();
var id = yield s.saveTx();
assert.typeOf(id, 'number');
// Remove condition
s = yield Zotero.Searches.getAsync(id);
yield s.loadConditions();
s.removeCondition(0);
var saved = yield s.save();
var saved = yield s.saveTx();
assert.isTrue(saved);
// Check saved search

View file

@ -39,7 +39,7 @@ describe("ZoteroPane", function() {
it("should update the item count", function* () {
var collection = new Zotero.Collection;
collection.name = "Count Test";
var id = yield collection.save();
var id = yield collection.saveTx();
yield waitForItemsLoad(win);
// Unselected, with no items in view
@ -51,7 +51,7 @@ describe("ZoteroPane", function() {
// Unselected, with one item in view
var item = new Zotero.Item('newspaperArticle');
item.setCollections([id]);
var itemID1 = yield item.save({
var itemID1 = yield item.saveTx({
skipSelect: true
});
assert.equal(
@ -62,7 +62,7 @@ describe("ZoteroPane", function() {
// Unselected, with multiple items in view
var item = new Zotero.Item('audioRecording');
item.setCollections([id]);
var itemID2 = yield item.save({
var itemID2 = yield item.saveTx({
skipSelect: true
});
assert.equal(