diff --git a/chrome/content/zotero/xpcom/data/creator.js b/chrome/content/zotero/xpcom/data/creator.js index cea69f6b6a..c869d64f1b 100644 --- a/chrome/content/zotero/xpcom/data/creator.js +++ b/chrome/content/zotero/xpcom/data/creator.js @@ -536,7 +536,7 @@ Zotero.Creator.prototype._checkValue = function (field, value) { break; case 'key': - if (!Zotero.ID.isValidKey(value)) { + if (!this._isValidKey(value)) { this._invalidValueError(field, value); } break; @@ -559,3 +559,8 @@ Zotero.Creator.prototype._generateKey = function () { Zotero.Creator.prototype._invalidValueError = function (field, value) { throw ("Invalid '" + field + "' value '" + value + "' in Zotero.Creator._invalidValueError()"); } + +Zotero.Creator.prototype._isValidKey = function (value) { + var re = /^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$/ + return re.test(value); +} diff --git a/chrome/content/zotero/xpcom/id.js b/chrome/content/zotero/xpcom/id.js index 4d68d79111..731dc31c5e 100644 --- a/chrome/content/zotero/xpcom/id.js +++ b/chrome/content/zotero/xpcom/id.js @@ -24,342 +24,43 @@ */ Zotero.ID_Tracker = function () { - this.get = get; - this.getBigInt = getBigInt; - this.skip = skip; - this.getTableName = getTableName; - - // Number of ids to compare against at a time - this.__defineGetter__('numIDs', function () 10000); - - // Number of times to try increasing the maxID if first range fails - this.__defineGetter__('maxTries', function () 3); - - // Total number of ids to find - this.__defineGetter__('maxToFind', function () 1000); - - var _available = {}; - var _min = {}; - var _skip = {}; + var _initialized = false; + var _tables = [ + 'collections', + 'creators', + 'creatorData', + 'customFields', + 'customItemTypes', + 'itemDataValues', + 'items', + 'savedSearches', + 'tags' + ]; + var _nextIDs = {}; - /* + function _init() { + Zotero.debug("Initializing ids"); + for (let table of _tables) { + _nextIDs[table] = _getNext(table); + } + _initialized = true; + } + + + /** * Gets an unused primary key id for a DB table */ - function get(table, notNull) { - table = this.getTableName(table); - - switch (table) { - // Autoincrement tables - // - // Callers need to handle a potential NULL for these unless they - // pass |notNull| - case 'items': - case 'creators': - case 'creatorData': - case 'collections': - case 'savedSearches': - case 'tags': - case 'customItemTypes': - case 'customFields': - var id = _getNextAvailable(table); - if (!id && notNull) { - return _getNext(table); - } - return id; - - // Non-autoincrement tables - // - // TODO: use autoincrement instead where available in 1.5 - case 'itemDataValues': - var id = _getNextAvailable(table); - if (!id) { - // If we can't find an empty id quickly, just use MAX() + 1 - return _getNext(table); - } - return id; - - default: - throw ("Unsupported table '" + table + "' in Zotero.ID.get()"); + this.get = function (table) { + if (!_initialized) { + _init(); } - } - - - this.isValidKey = function (value) { - var re = /^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$/ - return re.test(value); - } - - - function getBigInt(max) { - if (!max) { - max = 9007199254740991; - } - return Math.floor(Math.random() * (max)) + 1; - } - - - /** - * Mark ids as used - * - * @param string table - * @param int|array ids Item ids to skip - */ - function skip(table, ids) { - table = this.getTableName(table); - - switch (ids.constructor.name) { - case 'Array': - break; - - case 'Number': - ids = [ids]; - break; - - default: - throw ("ids must be an int or array of ints in Zotero.ID.skip()"); + if (!_nextIDs[table]) { + throw new Error("IDs not loaded for table '" + table + "'"); } - if (!ids.length) { - return; - } - - if (!_skip[table]) { - _skip[table] = {}; - } - - for (var i=0, len=ids.length; i max) { - max = parseInt(id); - } - } - if (!max) { - throw ("_skip['" + table + "'] must contain positive values in Zotero.ID._getNext()"); - } - sql += 'MAX(' + column + ', ' + max + ')'; - } - else { - sql += column; - } - sql += ')+1 FROM ' + table; - return Zotero.DB.valueQuery(sql); - } - - - /* - * Loads available ids for table into memory - */ - function _loadAvailable(table) { - Zotero.debug("Loading available ids for table '" + table + "'"); - - var minID = _min[table] ? _min[table] + 1 : 1; - var numIDs = Zotero.ID.numIDs; - var maxTries = Zotero.ID.maxTries; - var maxToFind = Zotero.ID.maxToFind; - - var column = _getTableColumn(table); - - switch (table) { - case 'creators': - case 'creatorData': - case 'items': - case 'itemDataValues': - case 'tags': - break; - - case 'collections': - case 'savedSearches': - case 'customItemTypes': - case 'customFields': - var maxToFind = 100; - break; - - default: - throw ("Unsupported table '" + table + "' in Zotero.ID._loadAvailable()"); - } - - var maxID = minID + numIDs - 1; - var sql = "SELECT " + column + " FROM " + table - + " WHERE " + column + " BETWEEN ? AND ? ORDER BY " + column; - var ids = Zotero.DB.columnQuery(sql, [minID, maxID]); - // If no ids found, we have numIDs unused ids - if (!ids) { - maxID = Math.min(maxID, minID + (maxToFind - 1)); - Zotero.debug("Found " + (maxID - minID + 1) + " available ids in table '" + table + "'"); - _available[table] = [[minID, maxID]]; - return; - } - - // If we didn't find any unused ids, try increasing maxID a few times - while (ids.length == numIDs && maxTries>0) { - Zotero.debug('No available ids found between ' + minID + ' and ' + maxID + '; trying next ' + numIDs); - minID = maxID + 1; - maxID = minID + numIDs - 1; - ids = Zotero.DB.columnQuery(sql, [minID, maxID]); - maxTries--; - } - - // Didn't find any unused ids -- _getNextAvailable() will return NULL for - // this table for rest of session - if (ids.length == numIDs) { - Zotero.debug("Found no available ids in table '" + table + "'"); - _available[table] = []; - return; - } - - var available = [], found = 0, j = 0, availableStart = null; - - for (var i=minID; i<=maxID && found i && i<=maxID) { - if (!availableStart) { - availableStart = i; - } - i++; - - if ((found + (i - availableStart) + 1) > maxToFind) { - break; - } - } - if (availableStart) { - available.push([availableStart, i-1]); - // Keep track of how many empties we've found - found += ((i-1) - availableStart) + 1; - availableStart = null; - } - j++; - } - - Zotero.debug("Found " + found + " available ids in table '" + table + "'"); - - _available[table] = available; - } - - - /** - * Find a unique random id for use in a DB table - * - * (No longer used) - **/ - function _getRandomID(table, max){ - var column = _getTableColumn(table); - - var sql = 'SELECT COUNT(*) FROM ' + table + ' WHERE ' + column + '= ?'; - - if (!max){ - max = 16383; - } - - max--; // since we use ceil(), decrement max by 1 - var tries = 3; // # of tries to find a unique id - for (var i=0; i} + */ + function _getNext(table) { + var sql = 'SELECT COALESCE(MAX(' + _getTableColumn(table) + ') + 1, 1) FROM ' + table; + return Zotero.DB.valueQuery(sql); + }; } Zotero.ID = new Zotero.ID_Tracker; - -/** - * Notifier observer to mark saved object ids as used - */ -Zotero.ID.EventListener = new function () { - this.init = init; - this.notify = notify; - - function init() { - Zotero.Notifier.registerObserver(this); - } - - - function notify(event, type, ids) { - if (event == 'add') { - try { - var table = Zotero.ID.getTableName(type); - } - // Skip if not a table we handle - catch (e) { - return; - } - Zotero.ID.skip(table, ids); - } - } -} -