diff --git a/chrome/chromeFiles/content/scholar/xpcom/schema.js b/chrome/chromeFiles/content/scholar/xpcom/schema.js index 9e48df837..7ede4ddaf 100644 --- a/chrome/chromeFiles/content/scholar/xpcom/schema.js +++ b/chrome/chromeFiles/content/scholar/xpcom/schema.js @@ -11,35 +11,51 @@ Scholar.Schema = new function(){ * Checks if the DB schema exists and is up-to-date, updating if necessary */ function updateSchema(){ - var dbVersion = _getDBVersion(); - var schemaVersion = _getSchemaSQLVersion(); - - if (dbVersion == schemaVersion){ - if (SCHOLAR_CONFIG['DB_REBUILD']){ - if (confirm('Erase all data and recreate database from schema?')){ - _initializeSchema(); - return; - } - } - - _updateScrapersLocal(); - return; - } - // If DB version is less than schema file, create or update - else if (dbVersion < schemaVersion){ - if (!dbVersion){ - Scholar.debug('Database does not exist -- creating\n'); + if (SCHOLAR_CONFIG['DB_REBUILD']){ + if (confirm('Erase all user data and recreate database from schema?')){ _initializeSchema(); return; } - - _migrateSchema(dbVersion); - _updateScrapersLocal(); + } + + var dbVersion = _getDBVersion('user'); + + // 'schema' check is for old (<= 1.0b1) schema system + if (!dbVersion && !_getDBVersion('schema')){ + Scholar.debug('Database does not exist -- creating\n'); + _initializeSchema(); return; } - else { - throw("Scholar DB version is newer than schema version"); + + // Old schema system + if (!dbVersion){ + dbVersion = 0; } + + var schemaVersion = _getSchemaSQLVersion('user'); + + if (dbVersion > schemaVersion){ + throw("Zotero user DB version is newer than SQL file"); + } + + Scholar.DB.beginTransaction(); + + try { + // If user DB version is less than schema file, create or update + if (dbVersion < schemaVersion){ + _migrateUserSchema(dbVersion); + } + + _updateSchema('system'); + _updateSchema('scrapers'); + Scholar.DB.commitTransaction(); + } + catch(e){ + Scholar.debug(e); + Scholar.DB.rollbackTransaction(); + throw(e); + } + return; } @@ -126,20 +142,8 @@ Scholar.Schema = new function(){ } if (Scholar.DB.tableExists('version')){ - try { - var dbVersion = Scholar.DB.valueQuery("SELECT version FROM " - + "version WHERE schema='" + schema + "'"); - } - // DEBUG: this is temporary to handle version table schema change - catch(e){ - if (e=='no such column: schema'){ - Scholar.debug(e, 1); - return false; - } - - // If some other problem, bail - throw(e); - } + var dbVersion = Scholar.DB.valueQuery("SELECT version FROM " + + "version WHERE schema='" + schema + "'"); _dbVersions[schema] = dbVersion; return dbVersion; } @@ -151,9 +155,8 @@ Scholar.Schema = new function(){ * Retrieve the version from the top line of the schema SQL file */ function _getSchemaSQLVersion(schema){ - // Default to schema.sql if (!schema){ - schema = 'schema'; + throw ('Schema type not provided to _getSchemaSQLVersion()'); } var schemaFile = schema + '.sql'; @@ -189,12 +192,11 @@ Scholar.Schema = new function(){ /* * Load in SQL schema * - * Returns an _array_ of SQL statements for feeding into query() + * Returns the contents of an SQL file for feeding into query() */ function _getSchemaSQL(schema){ - // Default to schema.sql if (!schema){ - schema = 'schema'; + throw ('Schema type not provided to _getSchemaSQL()'); } var schemaFile = schema + '.sql'; @@ -234,18 +236,21 @@ Scholar.Schema = new function(){ * Create new DB schema */ function _initializeSchema(){ + Scholar.DB.beginTransaction(); try { - Scholar.DB.beginTransaction(); - Scholar.DB.query(_getSchemaSQL()); - _updateDBVersion('schema', _getSchemaSQLVersion()); + Scholar.DB.query(_getSchemaSQL('user')); + _updateDBVersion('user', _getSchemaSQLVersion('user')); + Scholar.DB.query(_getSchemaSQL('system')); + _updateDBVersion('system', _getSchemaSQLVersion('system')); Scholar.DB.query(_getSchemaSQL('scrapers')); _updateDBVersion('scrapers', _getSchemaSQLVersion('scrapers')); Scholar.DB.commitTransaction(); } catch(e){ Scholar.debug(e, 1); - alert('Error initializing Scholar database'); // TODO: localize Scholar.DB.rollbackTransaction(); + alert('Error initializing Zotero database'); // TODO: localize + throw(e); } } @@ -260,25 +265,30 @@ Scholar.Schema = new function(){ } - /* - * Update the scrapers in the DB to the latest bundled versions - */ - function _updateScrapersLocal(){ - var dbVersion = _getDBVersion('scrapers'); - var schemaVersion = _getSchemaSQLVersion('scrapers'); + function _updateSchema(schema){ + var dbVersion = _getDBVersion(schema); + var schemaVersion = _getSchemaSQLVersion(schema); if (dbVersion == schemaVersion){ return; } else if (dbVersion < schemaVersion){ Scholar.DB.beginTransaction(); - Scholar.DB.query(_getSchemaSQL('scrapers')); - _updateDBVersion('scrapers', schemaVersion); - Scholar.DB.commitTransaction(); + try { + Scholar.DB.query(_getSchemaSQL(schema)); + _updateDBVersion(schema, schemaVersion); + Scholar.DB.commitTransaction(); + } + catch (e){ + Scholar.debug(e, 1); + Scholar.DB.rollbackTransaction(); + alert('Error updating Zotero database'); // TODO: localize + throw(e); + } return; } else { - throw("Scraper set in DB is newer than schema version"); + throw("Zotero '" + schema + "' DB version is newer than SQL file"); } } @@ -395,63 +405,40 @@ Scholar.Schema = new function(){ /* - * Migrate schema from an older version, preserving data + * Migrate user schema from an older version, preserving data */ - function _migrateSchema(fromVersion){ + function _migrateUserSchema(fromVersion){ // // Change this value to match the schema version // - var toVersion = 48; + var toVersion = 1; - if (toVersion != _getSchemaSQLVersion()){ - throw('Schema version does not match version in _migrateSchema()'); + if (toVersion != _getSchemaSQLVersion('user')){ + throw('User schema file version does not match version in _migrateUserSchema()'); } - Scholar.debug('Updating DB from version ' + fromVersion + ' to ' + toVersion + '\n'); + Scholar.debug('Updating user tables from version ' + fromVersion + ' to ' + toVersion); Scholar.DB.beginTransaction(); - // 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 (var i=fromVersion + 1; i<=toVersion; i++){ - if (i==30){ - // Remove old SQLite DB - var file = Scholar.getProfileDirectory(); - file.append('scholar.sqlite'); - if (file.exists()){ - file.remove(null); + try { + // 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 (var i=fromVersion + 1; i<=toVersion; i++){ + if (i==1){ + Scholar.DB.query("DELETE FROM version WHERE schema='schema'"); } } - if (i==47){ - // Clear storage directory - var file = Scholar.getStorageDirectory(); - if (file.exists()){ - file.remove(true); - } - _initializeSchema(); - } - - if(i==48) { - Scholar.DB.query('DROP TABLE IF EXISTS translators;\n' - +'CREATE TABLE translators (\n' - +' translatorID TEXT PRIMARY KEY,\n' - +' lastUpdated DATETIME,\n' - +' inRepository INT,\n' - +' priority INT,\n' - +' translatorType INT,\n' - +' label TEXT,\n' - +' creator TEXT,\n' - +' target TEXT,\n' - +' detectCode TEXT,\n' - +' code TEXT\n' - +');'); - } + _updateDBVersion('user', i-1); + Scholar.DB.commitTransaction(); + } + catch(e){ + Scholar.debug(e); + alert('Error migrating Zotero database'); + throw(e); } - - _updateDBVersion('schema', i-1); - Scholar.DB.commitTransaction(); } } diff --git a/schema.sql b/system.sql similarity index 82% rename from schema.sql rename to system.sql index 8f9c2959e..c1c55d7c1 100644 --- a/schema.sql +++ b/system.sql @@ -1,23 +1,9 @@ --- 48 +-- 1 + +-- This file creates system tables that can be safely wiped and reinitialized +-- at any time, as long as existing ids are preserved. + - DROP TABLE IF EXISTS version; - CREATE TABLE version ( - schema TEXT PRIMARY KEY, - version INT NOT NULL - ); - DROP INDEX IF EXISTS schema; - CREATE INDEX schema ON version(schema); - - -- The foundational table; every item collected has a unique record here - DROP TABLE IF EXISTS items; - CREATE TABLE items ( - itemID INTEGER PRIMARY KEY, - itemTypeID INT, - title TEXT, - dateAdded DATETIME DEFAULT CURRENT_TIMESTAMP, - dateModified DATETIME DEFAULT CURRENT_TIMESTAMP - ); - -- Valid item types ("book," "journalArticle," etc.) DROP TABLE IF EXISTS itemTypes; CREATE TABLE itemTypes ( @@ -55,32 +41,6 @@ FOREIGN KEY (fieldID) REFERENCES itemTypes(itemTypeID) ); - -- Type-specific data for individual items - DROP TABLE IF EXISTS itemData; - CREATE TABLE itemData ( - itemID INT, - fieldID INT, - value, - PRIMARY KEY (itemID, fieldID), - FOREIGN KEY (itemID) REFERENCES items(itemID), - FOREIGN KEY (fieldID) REFERENCES fields(fieldID) - ); - DROP INDEX IF EXISTS value; - CREATE INDEX value ON itemData(value); - - -- Note data for note items - DROP TABLE IF EXISTS itemNotes; - CREATE TABLE itemNotes ( - itemID INT, - sourceItemID INT, - note TEXT, - PRIMARY KEY (itemID), - FOREIGN KEY (itemID) REFERENCES items(itemID), - FOREIGN KEY (sourceItemID) REFERENCES items(itemID) - ); - DROP INDEX IF EXISTS itemNotes_sourceItemID; - CREATE INDEX itemNotes_sourceItemID ON itemNotes(sourceItemID); - DROP TABLE IF EXISTS charsets; CREATE TABLE charsets ( charsetID INTEGER PRIMARY KEY, @@ -107,66 +67,6 @@ DROP INDEX IF EXISTS fileTypeMimeTypes_mimeType; CREATE INDEX fileTypeMimeTypes_mimeType ON fileTypeMimeTypes(mimeType); - -- Metadata for attachment items - DROP TABLE IF EXISTS itemAttachments; - CREATE TABLE itemAttachments ( - itemID INT, - sourceItemID INT, - linkMode INT, - mimeType TEXT, - charsetID INT, - path TEXT, - originalPath TEXT, - PRIMARY KEY (itemID), - FOREIGN KEY (itemID) REFERENCES items(itemID), - FOREIGN KEY (sourceItemID) REFERENCES items(sourceItemID) - ); - DROP INDEX IF EXISTS itemAttachments_sourceItemID; - CREATE INDEX itemAttachments_sourceItemID ON itemAttachments(sourceItemID); - DROP INDEX IF EXISTS itemAttachments_mimeType; - CREATE INDEX itemAttachments_mimeType ON itemAttachments(mimeType); - - -- Individual entries for each tag - DROP TABLE IF EXISTS tags; - CREATE TABLE tags ( - tagID INT, - tag TEXT UNIQUE, - PRIMARY KEY (tagID) - ); - - -- Associates items with keywords - DROP TABLE IF EXISTS itemTags; - CREATE TABLE itemTags ( - itemID INT, - tagID INT, - PRIMARY KEY (itemID, tagID), - FOREIGN KEY (itemID) REFERENCES items(itemID), - FOREIGN KEY (tagID) REFERENCES tags(tagID) - ); - DROP INDEX IF EXISTS itemTags_tagID; - CREATE INDEX itemTags_tagID ON itemTags(tagID); - - DROP TABLE IF EXISTS itemSeeAlso; - CREATE TABLE itemSeeAlso ( - itemID INT, - linkedItemID INT, - PRIMARY KEY (itemID, linkedItemID), - FOREIGN KEY (itemID) REFERENCES items(itemID), - FOREIGN KEY (linkedItemID) REFERENCES items(itemID) - ); - DROP INDEX IF EXISTS itemSeeAlso_linkedItemID; - CREATE INDEX itemSeeAlso_linkedItemID ON itemSeeAlso(linkedItemID); - - -- Names of each individual "creator" (inc. authors, editors, etc.) - DROP TABLE IF EXISTS creators; - CREATE TABLE creators ( - creatorID INT, - firstName TEXT, - lastName TEXT, - isInstitution INT, - PRIMARY KEY (creatorID) - ); - -- Defines the possible creator types (contributor, editor, author) DROP TABLE IF EXISTS creatorTypes; CREATE TABLE creatorTypes ( @@ -174,61 +74,6 @@ creatorType TEXT ); - -- Associates single or multiple creators to items - DROP TABLE IF EXISTS itemCreators; - CREATE TABLE itemCreators ( - itemID INT, - creatorID INT, - creatorTypeID INT DEFAULT 1, - orderIndex INT DEFAULT 0, - PRIMARY KEY (itemID, creatorID, creatorTypeID, orderIndex), - FOREIGN KEY (itemID) REFERENCES items(itemID), - FOREIGN KEY (creatorID) REFERENCES creators(creatorID) - FOREIGN KEY (creatorTypeID) REFERENCES creatorTypes(creatorTypeID) - ); - - -- Collections for holding items - DROP TABLE IF EXISTS collections; - CREATE TABLE collections ( - collectionID INT, - collectionName TEXT, - parentCollectionID INT, - PRIMARY KEY (collectionID), - FOREIGN KEY (parentCollectionID) REFERENCES collections(collectionID) - ); - - -- Associates items with the various collections they belong to - DROP TABLE IF EXISTS collectionItems; - CREATE TABLE collectionItems ( - collectionID INT, - itemID INT, - orderIndex INT DEFAULT 0, - PRIMARY KEY (collectionID, itemID), - FOREIGN KEY (collectionID) REFERENCES collections(collectionID), - FOREIGN KEY (itemID) REFERENCES items(itemID) - ); - DROP INDEX IF EXISTS itemID; - CREATE INDEX itemID ON collectionItems(itemID); - - DROP TABLE IF EXISTS savedSearches; - CREATE TABLE savedSearches ( - savedSearchID INT, - savedSearchName TEXT, - PRIMARY KEY(savedSearchID) - ); - - DROP TABLE IF EXISTS savedSearchConditions; - CREATE TABLE savedSearchConditions ( - savedSearchID INT, - searchConditionID INT, - condition TEXT, - operator TEXT, - value TEXT, - required NONE, - PRIMARY KEY(savedSearchID, searchConditionID), - FOREIGN KEY (savedSearchID) REFERENCES savedSearches(savedSearchID) - ); - DROP TABLE IF EXISTS translators; CREATE TABLE translators ( translatorID TEXT PRIMARY KEY, @@ -678,6 +523,4 @@ INSERT INTO "creatorTypes" VALUES(2, "contributor"); INSERT INTO "creatorTypes" VALUES(3, "editor"); INSERT INTO "creatorTypes" VALUES(4, "translator"); - - INSERT INTO "items" VALUES(1233, 14, 'Zotero - Quick Start Guide', '2006-08-31 20:00:00', '2006-08-31 20:00:00'); - INSERT INTO "itemAttachments" VALUES(1233, NULL, 3, 'text/html', 25, 'http://www.zotero.org/docs/quick_start_guide.php', NULL); + diff --git a/user.sql b/user.sql new file mode 100644 index 000000000..ce3be64a5 --- /dev/null +++ b/user.sql @@ -0,0 +1,169 @@ +-- 1 + +-- This file creates tables containing user-specific data -- any changes made +-- here must be mirrored in transition steps in schema.js::_migrateSchema(), +-- as this file will only be used for new users. + + +DROP TABLE IF EXISTS version; +CREATE TABLE version ( + schema TEXT PRIMARY KEY, + version INT NOT NULL +); +DROP INDEX IF EXISTS schema; +CREATE INDEX schema ON version(schema); + +-- The foundational table; every item collected has a unique record here +DROP TABLE IF EXISTS items; +CREATE TABLE items ( + itemID INTEGER PRIMARY KEY, + itemTypeID INT, + title TEXT, + dateAdded DATETIME DEFAULT CURRENT_TIMESTAMP, + dateModified DATETIME DEFAULT CURRENT_TIMESTAMP +); + +-- Type-specific data for individual items +DROP TABLE IF EXISTS itemData; +CREATE TABLE itemData ( + itemID INT, + fieldID INT, + value, + PRIMARY KEY (itemID, fieldID), + FOREIGN KEY (itemID) REFERENCES items(itemID), + FOREIGN KEY (fieldID) REFERENCES fields(fieldID) +); +DROP INDEX IF EXISTS value; +CREATE INDEX value ON itemData(value); + +-- Note data for note items +DROP TABLE IF EXISTS itemNotes; +CREATE TABLE itemNotes ( + itemID INT, + sourceItemID INT, + note TEXT, + PRIMARY KEY (itemID), + FOREIGN KEY (itemID) REFERENCES items(itemID), + FOREIGN KEY (sourceItemID) REFERENCES items(itemID) +); +DROP INDEX IF EXISTS itemNotes_sourceItemID; +CREATE INDEX itemNotes_sourceItemID ON itemNotes(sourceItemID); + +-- Metadata for attachment items +DROP TABLE IF EXISTS itemAttachments; +CREATE TABLE itemAttachments ( + itemID INT, + sourceItemID INT, + linkMode INT, + mimeType TEXT, + charsetID INT, + path TEXT, + originalPath TEXT, + PRIMARY KEY (itemID), + FOREIGN KEY (itemID) REFERENCES items(itemID), + FOREIGN KEY (sourceItemID) REFERENCES items(sourceItemID) +); +DROP INDEX IF EXISTS itemAttachments_sourceItemID; +CREATE INDEX itemAttachments_sourceItemID ON itemAttachments(sourceItemID); +DROP INDEX IF EXISTS itemAttachments_mimeType; +CREATE INDEX itemAttachments_mimeType ON itemAttachments(mimeType); + +-- Individual entries for each tag +DROP TABLE IF EXISTS tags; +CREATE TABLE tags ( + tagID INT, + tag TEXT UNIQUE, + PRIMARY KEY (tagID) +); + +-- Associates items with keywords +DROP TABLE IF EXISTS itemTags; +CREATE TABLE itemTags ( + itemID INT, + tagID INT, + PRIMARY KEY (itemID, tagID), + FOREIGN KEY (itemID) REFERENCES items(itemID), + FOREIGN KEY (tagID) REFERENCES tags(tagID) +); +DROP INDEX IF EXISTS itemTags_tagID; +CREATE INDEX itemTags_tagID ON itemTags(tagID); + +DROP TABLE IF EXISTS itemSeeAlso; +CREATE TABLE itemSeeAlso ( + itemID INT, + linkedItemID INT, + PRIMARY KEY (itemID, linkedItemID), + FOREIGN KEY (itemID) REFERENCES items(itemID), + FOREIGN KEY (linkedItemID) REFERENCES items(itemID) +); +DROP INDEX IF EXISTS itemSeeAlso_linkedItemID; +CREATE INDEX itemSeeAlso_linkedItemID ON itemSeeAlso(linkedItemID); + +-- Names of each individual "creator" (inc. authors, editors, etc.) +DROP TABLE IF EXISTS creators; +CREATE TABLE creators ( + creatorID INT, + firstName TEXT, + lastName TEXT, + isInstitution INT, + PRIMARY KEY (creatorID) +); + +-- Associates single or multiple creators to items +DROP TABLE IF EXISTS itemCreators; +CREATE TABLE itemCreators ( + itemID INT, + creatorID INT, + creatorTypeID INT DEFAULT 1, + orderIndex INT DEFAULT 0, + PRIMARY KEY (itemID, creatorID, creatorTypeID, orderIndex), + FOREIGN KEY (itemID) REFERENCES items(itemID), + FOREIGN KEY (creatorID) REFERENCES creators(creatorID) + FOREIGN KEY (creatorTypeID) REFERENCES creatorTypes(creatorTypeID) +); + +-- Collections for holding items +DROP TABLE IF EXISTS collections; +CREATE TABLE collections ( + collectionID INT, + collectionName TEXT, + parentCollectionID INT, + PRIMARY KEY (collectionID), + FOREIGN KEY (parentCollectionID) REFERENCES collections(collectionID) +); + +-- Associates items with the various collections they belong to +DROP TABLE IF EXISTS collectionItems; +CREATE TABLE collectionItems ( + collectionID INT, + itemID INT, + orderIndex INT DEFAULT 0, + PRIMARY KEY (collectionID, itemID), + FOREIGN KEY (collectionID) REFERENCES collections(collectionID), + FOREIGN KEY (itemID) REFERENCES items(itemID) +); +DROP INDEX IF EXISTS itemID; +CREATE INDEX itemID ON collectionItems(itemID); + +DROP TABLE IF EXISTS savedSearches; +CREATE TABLE savedSearches ( + savedSearchID INT, + savedSearchName TEXT, + PRIMARY KEY(savedSearchID) +); + +DROP TABLE IF EXISTS savedSearchConditions; +CREATE TABLE savedSearchConditions ( + savedSearchID INT, + searchConditionID INT, + condition TEXT, + operator TEXT, + value TEXT, + required NONE, + PRIMARY KEY(savedSearchID, searchConditionID), + FOREIGN KEY (savedSearchID) REFERENCES savedSearches(savedSearchID) +); + +INSERT INTO "items" VALUES(1233, 14, 'Zotero - Quick Start Guide', '2006-08-31 20:00:00', '2006-08-31 20:00:00'); +INSERT INTO "itemAttachments" VALUES(1233, NULL, 3, 'text/html', 25, 'http://www.zotero.org/docs/quick_start_guide.php', NULL); +