fix: fix npe on pp after rename folder (#35260)#35298
fix: fix npe on pp after rename folder (#35260)#35298gortiz-dotcms wants to merge 4 commits intomainfrom
Conversation
|
Claude finished @gortiz-dotcms's task in 3m 59s —— View job 🔍 dotCMS Backend Review
3 findings posted — all 🟡 Medium. Security and REST API sub-agents returned no issues. Full review in the separate backend review comment. |
🔍 dotCMS Backend Review[🟡 Medium]
Logger.debug(FolderFactoryImpl.class,
"Bumped version_ts and evicted version-info cache for " + affected.size()
+ " contentlet(s) under path '" + rootPath + "'"); // line 1092-1094
// Same pattern at line 1123-1124 in evictContentletCacheForSubtree()💡 Strip CR/LF before logging: [🟡 Medium]
// Round-trip 1: SELECT i.id FROM identifier i WHERE parent_path LIKE ? …
final List<Map<String, Object>> affected = new DotConnect().setSQL("SELECT i.id …").loadObjectResults();
// Round-trip 2: UPDATE contentlet_version_info … WHERE identifier IN (SELECT i.id … same WHERE)
new DotConnect().executeUpdate("UPDATE contentlet_version_info … WHERE identifier IN (SELECT …)", …);💡 Collapse into one query using Next steps
|
…UID behavior Child subfolder identifiers are no longer preserved after a parent rename — each subfolder gets a new deterministic UUID (path change = identity change), so the old UUIDs are deleted. Updated both renameFolder tests to assert the new behavior: old identifiers are null, sub-folders are findable by new path with new UUIDs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…E order fix - Add version_ts assertion to renameFolder_updatesChildrenAndSubChildrenPaths: captures a timestamp before the rename and asserts all three contentlets (file in parent, page in parent, file in sub-folder) have version_ts > beforeRename, protecting the core push-publish fix against regression. - Flip SELECT/UPDATE order in bumpVersionTsForSubtree: run the UPDATE first, then SELECT affected rows for cache eviction. The previous order had a race window where a concurrent INSERT between SELECT and UPDATE would be bumped but its cache entry would not be evicted. Running UPDATE first closes that gap. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix ClassCastException in test: use loadObjectResults() instead of loadResults() so version_ts comes back as Timestamp, not String - Fix variant mismatch (High): restrict UPDATE to DEFAULT variant only (AND variant_id = ?) so the UPDATE scope aligns exactly with the cache eviction, which already targets DEFAULT via removeContentletVersionInfoToCache(id, lang) - Fix duplicate round-trips (Medium): SELECT affected identifiers first, then feed the collected ID list into the UPDATE via an explicit IN (...) to avoid scanning the identifier table twice Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔍 dotCMS Backend Review[🟡 Medium]
final String placeholders = ids.stream().map(id -> "?").collect(Collectors.joining(", "));
final Object[] params = new Object[ids.size() + 2];
// ...all ids in one shot...
new DotConnect().executeUpdate(
"UPDATE contentlet_version_info SET version_ts = ? WHERE identifier IN (" + placeholders + ") ...", params);💡 Partition [🟡 Medium]
Logger.debug(FolderFactoryImpl.class,
"Bumped version_ts and evicted version-info cache for " + ids.size()
+ " contentlet(s) under path '" + rootPath + "'"); // rootPath contains user-supplied newName💡 Strip CR/LF before logging: [🟡 Medium]
params[0] = new Date(); // java.util.Date — should be java.sql.Timestamp💡 Replace with Next steps
|
This PR fixes: #35260
Problem
Push-publishing a renamed folder (with content inside, or with child subfolders) failed in two ways:
PushPublishigDependencyProcesorlooked up the folder by its old identifier, which no longer existed after rename, producing aNullPointerExceptiononfolder.getPath().version_tswas not updated after the rename, so push-publish considered them unchanged. The same applied to content inside child subfolders.Root cause
These problems pre-date the recent folder-rename refactors (PRs #35086 and #35176). The rename workflow replaced the original per-item
move()approach with a bulk SQL update ofidentifier.parent_path, but did not replicate two side effects thatmove()performed automatically:contentlet_version_info.version_tsso push-publish detects the content as changed.Fix
Content not detected as changed
A bulk
UPDATE contentlet_version_info SET version_tsis issued for all non-folder assets under the renamed path, followed by targetedIdentifierCacheeviction per language. This replicates what the old per-contentletmove()did, but in a single SQL statement.Child subfolder identity
Folder identifiers in dotCMS are deterministic: the UUID is a hash of
assetType:hostname:parentPath:folderName. When a parent folder is renamed, every child subfolder's path changes — meaning each child must receive a new identifier. This was the original behavior before the rename refactors.The fix restores that original approach: for each child subfolder (sorted parent-before-child so
findFolderByPathresolves correctly),getNewFolderRecord()is called to create a new record with a new deterministic UUID. The bulkupdateChildPathsSQL is kept for non-folder identifiers only (AND asset_type != 'folder'), and old child folder records are deleted in child-before-parent order to satisfy thecheck_child_assetsDB trigger. Because every folder in the push bundle now carries a new UUID, the receiver always takes the create path inFolderHandler— nomove()cascade, no conflict.Removed
bumpSubFolderModDateForSubtreeA previous attempt bumped
folder.mod_datefor child subfolders to force push-publish to include them. This is no longer needed:getNewFolderRecord()setsmodDate = new Date(), so every new child folder record is automatically newer than any previous push date and will be included without manual bumping. The bump also caused the "Conflicts between Folders" receiver error and has been removed.