diff --git a/toolkit/components/places/src/PlacesDBUtils.jsm b/toolkit/components/places/src/PlacesDBUtils.jsm index 6d88e25237a..0b5310882ca 100644 --- a/toolkit/components/places/src/PlacesDBUtils.jsm +++ b/toolkit/components/places/src/PlacesDBUtils.jsm @@ -303,7 +303,7 @@ let PlacesDBUtils = { // MOZ_ANNO_ATTRIBUTES // A.1 remove unused attributes - let deleteUnusedAnnoAttributes = DBConn.createAsyncStatement( + let deleteUnusedAnnoAttributes = DBConn.createStatement( "DELETE FROM moz_anno_attributes WHERE id IN ( " + "SELECT id FROM moz_anno_attributes n " + "WHERE NOT EXISTS " + @@ -315,7 +315,7 @@ let PlacesDBUtils = { // MOZ_ANNOS // B.1 remove annos with an invalid attribute - let deleteInvalidAttributeAnnos = DBConn.createAsyncStatement( + let deleteInvalidAttributeAnnos = DBConn.createStatement( "DELETE FROM moz_annos WHERE id IN ( " + "SELECT id FROM moz_annos a " + "WHERE NOT EXISTS " + @@ -325,7 +325,7 @@ let PlacesDBUtils = { cleanupStatements.push(deleteInvalidAttributeAnnos); // B.2 remove orphan annos - let deleteOrphanAnnos = DBConn.createAsyncStatement( + let deleteOrphanAnnos = DBConn.createStatement( "DELETE FROM moz_annos WHERE id IN ( " + "SELECT id FROM moz_annos a " + "WHERE NOT EXISTS " + @@ -342,7 +342,7 @@ let PlacesDBUtils = { selectPlacesRoot.params["places_root"] = PlacesUtils.placesRootId; if (!selectPlacesRoot.executeStep()) { // We are missing the root, try to recreate it. - let createPlacesRoot = DBConn.createAsyncStatement( + let createPlacesRoot = DBConn.createStatement( "INSERT INTO moz_bookmarks (id, type, fk, parent, position, title, " + "guid) " + "VALUES (:places_root, 2, NULL, 0, 0, :title, GENERATE_GUID())"); @@ -351,7 +351,7 @@ let PlacesDBUtils = { cleanupStatements.push(createPlacesRoot); // Now ensure that other roots are children of Places root. - let fixPlacesRootChildren = DBConn.createAsyncStatement( + let fixPlacesRootChildren = DBConn.createStatement( "UPDATE moz_bookmarks SET parent = :places_root WHERE id IN " + "(SELECT folder_id FROM moz_bookmarks_roots " + "WHERE folder_id <> :places_root)"); @@ -366,30 +366,30 @@ let PlacesDBUtils = { let updateRootTitleSql = "UPDATE moz_bookmarks SET title = :title " + "WHERE id = :root_id AND title <> :title"; // root - let fixPlacesRootTitle = DBConn.createAsyncStatement(updateRootTitleSql); + let fixPlacesRootTitle = DBConn.createStatement(updateRootTitleSql); fixPlacesRootTitle.params["root_id"] = PlacesUtils.placesRootId; fixPlacesRootTitle.params["title"] = ""; cleanupStatements.push(fixPlacesRootTitle); // bookmarks menu - let fixBookmarksMenuTitle = DBConn.createAsyncStatement(updateRootTitleSql); + let fixBookmarksMenuTitle = DBConn.createStatement(updateRootTitleSql); fixBookmarksMenuTitle.params["root_id"] = PlacesUtils.bookmarksMenuFolderId; fixBookmarksMenuTitle.params["title"] = PlacesUtils.getString("BookmarksMenuFolderTitle"); cleanupStatements.push(fixBookmarksMenuTitle); // bookmarks toolbar - let fixBookmarksToolbarTitle = DBConn.createAsyncStatement(updateRootTitleSql); + let fixBookmarksToolbarTitle = DBConn.createStatement(updateRootTitleSql); fixBookmarksToolbarTitle.params["root_id"] = PlacesUtils.toolbarFolderId; fixBookmarksToolbarTitle.params["title"] = PlacesUtils.getString("BookmarksToolbarFolderTitle"); cleanupStatements.push(fixBookmarksToolbarTitle); // unsorted bookmarks - let fixUnsortedBookmarksTitle = DBConn.createAsyncStatement(updateRootTitleSql); + let fixUnsortedBookmarksTitle = DBConn.createStatement(updateRootTitleSql); fixUnsortedBookmarksTitle.params["root_id"] = PlacesUtils.unfiledBookmarksFolderId; fixUnsortedBookmarksTitle.params["title"] = PlacesUtils.getString("UnsortedBookmarksFolderTitle"); cleanupStatements.push(fixUnsortedBookmarksTitle); // tags - let fixTagsRootTitle = DBConn.createAsyncStatement(updateRootTitleSql); + let fixTagsRootTitle = DBConn.createStatement(updateRootTitleSql); fixTagsRootTitle.params["root_id"] = PlacesUtils.tagsFolderId; fixTagsRootTitle.params["title"] = PlacesUtils.getString("TagsFolderTitle"); @@ -398,7 +398,7 @@ let PlacesDBUtils = { // MOZ_BOOKMARKS // D.1 remove items without a valid place // if fk IS NULL we fix them in D.7 - let deleteNoPlaceItems = DBConn.createAsyncStatement( + let deleteNoPlaceItems = DBConn.createStatement( "DELETE FROM moz_bookmarks WHERE id NOT IN ( " + "SELECT folder_id FROM moz_bookmarks_roots " + // skip roots ") AND id IN (" + @@ -410,7 +410,7 @@ let PlacesDBUtils = { cleanupStatements.push(deleteNoPlaceItems); // D.2 remove items that are not uri bookmarks from tag containers - let deleteBogusTagChildren = DBConn.createAsyncStatement( + let deleteBogusTagChildren = DBConn.createStatement( "DELETE FROM moz_bookmarks WHERE id NOT IN ( " + "SELECT folder_id FROM moz_bookmarks_roots " + // skip roots ") AND id IN (" + @@ -424,7 +424,7 @@ let PlacesDBUtils = { cleanupStatements.push(deleteBogusTagChildren); // D.3 remove empty tags - let deleteEmptyTags = DBConn.createAsyncStatement( + let deleteEmptyTags = DBConn.createStatement( "DELETE FROM moz_bookmarks WHERE id NOT IN ( " + "SELECT folder_id FROM moz_bookmarks_roots " + // skip roots ") AND id IN (" + @@ -438,7 +438,7 @@ let PlacesDBUtils = { cleanupStatements.push(deleteEmptyTags); // D.4 move orphan items to unsorted folder - let fixOrphanItems = DBConn.createAsyncStatement( + let fixOrphanItems = DBConn.createStatement( "UPDATE moz_bookmarks SET parent = :unsorted_folder WHERE id NOT IN ( " + "SELECT folder_id FROM moz_bookmarks_roots " + // skip roots ") AND id IN (" + @@ -451,7 +451,7 @@ let PlacesDBUtils = { cleanupStatements.push(fixOrphanItems); // D.5 fix wrong keywords - let fixInvalidKeywords = DBConn.createAsyncStatement( + let fixInvalidKeywords = DBConn.createStatement( "UPDATE moz_bookmarks SET keyword_id = NULL WHERE id NOT IN ( " + "SELECT folder_id FROM moz_bookmarks_roots " + // skip roots ") AND id IN ( " + @@ -466,7 +466,7 @@ let PlacesDBUtils = { // Folders, separators and dynamic containers should not have an fk. // If they have a valid fk convert them to bookmarks. Later in D.9 we // will move eventual children to unsorted bookmarks. - let fixBookmarksAsFolders = DBConn.createAsyncStatement( + let fixBookmarksAsFolders = DBConn.createStatement( "UPDATE moz_bookmarks SET type = :bookmark_type WHERE id NOT IN ( " + "SELECT folder_id FROM moz_bookmarks_roots " + // skip roots ") AND id IN ( " + @@ -483,7 +483,7 @@ let PlacesDBUtils = { // D.7 fix wrong item types // Bookmarks should have an fk, if they don't have any, convert them to // folders. - let fixFoldersAsBookmarks = DBConn.createAsyncStatement( + let fixFoldersAsBookmarks = DBConn.createStatement( "UPDATE moz_bookmarks SET type = :folder_type WHERE id NOT IN ( " + "SELECT folder_id FROM moz_bookmarks_roots " + // skip roots ") AND id IN ( " + @@ -498,7 +498,7 @@ let PlacesDBUtils = { // D.8 fix wrong item types // Dynamic containers should have a folder_type, if they don't have any // convert them to folders. - let fixFoldersAsDynamic = DBConn.createAsyncStatement( + let fixFoldersAsDynamic = DBConn.createStatement( "UPDATE moz_bookmarks SET type = :folder_type WHERE id NOT IN ( " + "SELECT folder_id FROM moz_bookmarks_roots " + // skip roots ") AND id IN ( " + @@ -513,7 +513,7 @@ let PlacesDBUtils = { // D.9 fix wrong parents // Items cannot have dynamic containers, separators or other bookmarks // as parent, if they have bad parent move them to unsorted bookmarks. - let fixInvalidParents = DBConn.createAsyncStatement( + let fixInvalidParents = DBConn.createStatement( "UPDATE moz_bookmarks SET parent = :unsorted_folder WHERE id NOT IN ( " + "SELECT folder_id FROM moz_bookmarks_roots " + // skip roots ") AND id IN ( " + @@ -529,53 +529,40 @@ let PlacesDBUtils = { fixInvalidParents.params["dynamic_type"] = PlacesUtils.bookmarks.TYPE_DYNAMIC_CONTAINER; cleanupStatements.push(fixInvalidParents); +/* XXX needs test // D.10 recalculate positions // This requires multiple related statements. // We can detect a folder with bad position values comparing the sum of - // all distinct position values (+1 since position is 0-based) with the - // triangular numbers obtained by the number of children (n). - // SUM(DISTINCT position + 1) == (n * (n + 1) / 2). - cleanupStatements.push(DBConn.createAsyncStatement( - "CREATE TEMP TABLE moz_bookmarks_reposition_temp ( " + - " id INTEGER PRIMARY_KEY " + - ", position INTEGER " + - ") " - )); - cleanupStatements.push(DBConn.createAsyncStatement( - "INSERT INTO moz_bookmarks_reposition_temp " + - "SELECT id, (" + - "(SELECT count(*) FROM moz_bookmarks " + - "WHERE parent = b.parent " + - "AND position < b.position) + " + - "(SELECT count(*) FROM moz_bookmarks " + - "WHERE parent = b.parent " + - "AND position == b.position " + - "AND ROWID < b.ROWID) " + - ") AS reposition " + - "FROM moz_bookmarks b " + - "WHERE parent IN (" + - "SELECT parent FROM " + - "(SELECT parent, " + - "(SUM(DISTINCT position + 1) - (count(*) * (count(*) + 1) / 2)) AS diff " + - "FROM moz_bookmarks " + - "GROUP BY parent) " + - "WHERE diff <> 0" + - ") " - )); - cleanupStatements.push(DBConn.createAsyncStatement( - "UPDATE moz_bookmarks SET position = " + - "(SELECT position FROM moz_bookmarks_reposition_temp " + - "WHERE id = moz_bookmarks.id) " + - "WHERE id IN (SELECT id FROM moz_bookmarks_reposition_temp) " - )); - cleanupStatements.push(DBConn.createAsyncStatement( - "DROP TABLE moz_bookmarks_reposition_temp" - )); + // all position values with the triangular numbers obtained by the number + // of children: (n * (n + 1) / 2). Starting from 0 is (n * (n - 1) / 2). + let detectWrongPositionsParents = DBConn.createStatement( + "SELECT parent FROM " + + "(SELECT parent, " + + "(SUM(position) - (count(*) * (count(*) - 1) / 2)) AS diff " + + "FROM moz_bookmarks " + + "GROUP BY parent) " + + "WHERE diff <> 0"); + while (detectWrongPositionsParents.executeStep()) { + let parent = detectWrongPositionsParents.getInt64(0); + // We will lose the previous position values and reposition items based + // on the ROWID value. Not perfect, but we can't rely on position values. + let fixPositionsForParent = DBConn.createStatement( + "UPDATE moz_bookmarks SET position = ( " + + "SELECT " + + "((SELECT count(*) FROM moz_bookmarks WHERE parent = :parent) - " + + "(SELECT count(*) FROM moz_bookmarks " + + "WHERE parent = :parent AND ROWID >= b.ROWID)) " + + "FROM moz_bookmarks b WHERE parent = :parent AND id = moz_bookmarks.id " + + ") WHERE parent = :parent"); + fixPositionsForParent.params["parent"] = parent; + cleanupStatements.push(fixPositionsForParent); + } +*/ // D.11 remove old livemarks status items // Livemark status items are now static but some livemark has still old // status items bookmarks inside it. We should remove them. - let removeLivemarkStaticItems = DBConn.createAsyncStatement( + let removeLivemarkStaticItems = DBConn.createStatement( "DELETE FROM moz_bookmarks WHERE type = :bookmark_type AND fk IN ( " + "SELECT id FROM moz_places WHERE url = :lmloading OR url = :lmfailed " + ")"); @@ -587,7 +574,7 @@ let PlacesDBUtils = { // D.12 Fix empty-named tags. // Tags were allowed to have empty names due to a UI bug. Fix them // replacing their title with "(notitle)". - let fixEmptyNamedTags = DBConn.createAsyncStatement( + let fixEmptyNamedTags = DBConn.createStatement( "UPDATE moz_bookmarks SET title = :empty_title " + "WHERE length(title) = 0 AND type = :folder_type " + "AND parent = :tags_folder" @@ -599,7 +586,7 @@ let PlacesDBUtils = { // MOZ_FAVICONS // E.1 remove orphan icons - let deleteOrphanIcons = DBConn.createAsyncStatement( + let deleteOrphanIcons = DBConn.createStatement( "DELETE FROM moz_favicons WHERE id IN (" + "SELECT id FROM moz_favicons f " + "WHERE NOT EXISTS " + @@ -609,7 +596,7 @@ let PlacesDBUtils = { // MOZ_HISTORYVISITS // F.1 remove orphan visits - let deleteOrphanVisits = DBConn.createAsyncStatement( + let deleteOrphanVisits = DBConn.createStatement( "DELETE FROM moz_historyvisits WHERE id IN (" + "SELECT id FROM moz_historyvisits v " + "WHERE NOT EXISTS " + @@ -619,7 +606,7 @@ let PlacesDBUtils = { // MOZ_INPUTHISTORY // G.1 remove orphan input history - let deleteOrphanInputHistory = DBConn.createAsyncStatement( + let deleteOrphanInputHistory = DBConn.createStatement( "DELETE FROM moz_inputhistory WHERE place_id IN (" + "SELECT place_id FROM moz_inputhistory i " + "WHERE NOT EXISTS " + @@ -629,7 +616,7 @@ let PlacesDBUtils = { // MOZ_ITEMS_ANNOS // H.1 remove item annos with an invalid attribute - let deleteInvalidAttributeItemsAnnos = DBConn.createAsyncStatement( + let deleteInvalidAttributeItemsAnnos = DBConn.createStatement( "DELETE FROM moz_items_annos WHERE id IN ( " + "SELECT id FROM moz_items_annos t " + "WHERE NOT EXISTS " + @@ -639,7 +626,7 @@ let PlacesDBUtils = { cleanupStatements.push(deleteInvalidAttributeItemsAnnos); // H.2 remove orphan item annos - let deleteOrphanItemsAnnos = DBConn.createAsyncStatement( + let deleteOrphanItemsAnnos = DBConn.createStatement( "DELETE FROM moz_items_annos WHERE id IN ( " + "SELECT id FROM moz_items_annos t " + "WHERE NOT EXISTS " + @@ -649,7 +636,7 @@ let PlacesDBUtils = { // MOZ_KEYWORDS // I.1 remove unused keywords - let deleteUnusedKeywords = DBConn.createAsyncStatement( + let deleteUnusedKeywords = DBConn.createStatement( "DELETE FROM moz_keywords WHERE id IN ( " + "SELECT id FROM moz_keywords k " + "WHERE NOT EXISTS " + @@ -659,7 +646,7 @@ let PlacesDBUtils = { // MOZ_PLACES // L.1 fix wrong favicon ids - let fixInvalidFaviconIds = DBConn.createAsyncStatement( + let fixInvalidFaviconIds = DBConn.createStatement( "UPDATE moz_places SET favicon_id = NULL WHERE id IN ( " + "SELECT id FROM moz_places h " + "WHERE favicon_id NOT NULL " + diff --git a/toolkit/components/places/tests/unit/test_preventive_maintenance.js b/toolkit/components/places/tests/unit/test_preventive_maintenance.js index a60544f690c..2984b4fa86a 100644 --- a/toolkit/components/places/tests/unit/test_preventive_maintenance.js +++ b/toolkit/components/places/tests/unit/test_preventive_maintenance.js @@ -694,74 +694,17 @@ tests.push({ }); //------------------------------------------------------------------------------ - +//XXX TODO tests.push({ name: "D.10", desc: "Recalculate positions", - _bookmarks: [], - setup: function() { - const NUM_BOOKMARKS = 30; - bs.runInBatchMode({ - runBatched: function (aUserData) { - for (let i = 0; i < NUM_BOOKMARKS; i++) { - bs.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, - NetUtil.newURI("http://example.com/"), - bs.DEFAULT_INDEX, "testbookmark"); - } - } - }, null); - // Set random positions for the added bookmarks. - let stmt = mDBConn.createStatement( - "UPDATE moz_bookmarks SET position = :rand " + - "WHERE id IN ( " + - "SELECT id FROM moz_bookmarks WHERE parent = :unfiled " + - "ORDER BY RANDOM() LIMIT 1 " + - ") " - ); - for (let i = 0; i < (NUM_BOOKMARKS / 2); i++) { - stmt.params["unfiled"] = PlacesUtils.unfiledBookmarksFolderId; - stmt.params["rand"] = Math.round(Math.random() * (NUM_BOOKMARKS - 1)); - stmt.execute(); - stmt.reset(); - } - stmt.finalize(); - - // Build the expected ordered list of bookmarks. - stmt = mDBConn.createStatement( - "SELECT id, position " + - "FROM moz_bookmarks WHERE parent = :unfiled " + - "ORDER BY position ASC, ROWID ASC " - ); - stmt.params["unfiled"] = PlacesUtils.unfiledBookmarksFolderId; - while (stmt.executeStep()) { - this._bookmarks.push(stmt.row.id); - print(stmt.row.id + "\t" + stmt.row.position + "\t" + (this._bookmarks.length - 1)); - } - stmt.finalize(); }, check: function() { - // Build the expected ordered list of bookmarks. - let stmt = mDBConn.createStatement( - "SELECT id, position FROM moz_bookmarks WHERE parent = :unfiled " + - "ORDER BY position ASC" - ); - stmt.params["unfiled"] = PlacesUtils.unfiledBookmarksFolderId; - let pass = true; - while (stmt.executeStep()) { - print(stmt.row.id + "\t" + stmt.row.position); - if (this._bookmarks.indexOf(stmt.row.id) != stmt.row.position) { - pass = false; - } - } - if (!pass) { - dump_table("moz_bookmarks"); - do_throw("Unexpected bookmarks order."); - } - stmt.finalize(); + } });