From d2e95e0af86d65f9621fcfcb55bd51b403074415 Mon Sep 17 00:00:00 2001 From: Ondrej Kosarko Date: Tue, 11 Aug 2026 16:13:57 +0200 Subject: [PATCH] Issue 1406: normalize metadatavalue.text_lang = '*' to NULL Rows written before the 7.6.5 upgrade carry text_lang = '*' (Item.ANY). MetadataValue.setLanguage() normalizes '*' -> NULL since 7.6.5, so no new rows can be created, but nothing repaired the existing ones: the setter only runs on row creation. Stale '*' rows break the metadata-export -> metadata-import round trip (the export emits [*] headers the import rejects, #1402) and make OAI store , from which oai_openaire emits an invalid xml:lang="*". The migration bumps item.last_modified on the affected items before normalizing. That is what makes the reindex happen without a forced full rebuild: 'dspace oai import' and 'dspace index-discovery' both select items by last_modified, so the existing cron runs pick up exactly the touched items. Only '*' is normalized; empty-string languages are legal and round-trip correctly. (cherry picked from commit 442fae67f31d629e538cae052f64fcc7c9a5d63f) --- ...026.08.11__Normalize_metadata_language.sql | 34 +++++++++++++++++++ ...026.08.11__Normalize_metadata_language.sql | 34 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2026.08.11__Normalize_metadata_language.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2026.08.11__Normalize_metadata_language.sql diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2026.08.11__Normalize_metadata_language.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2026.08.11__Normalize_metadata_language.sql new file mode 100644 index 00000000000..66593f23601 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2026.08.11__Normalize_metadata_language.sql @@ -0,0 +1,34 @@ +-- +-- The contents of this file are subject to the license and copyright +-- detailed in the LICENSE and NOTICE files at the root of the source +-- tree and available online at +-- +-- http://www.dspace.org/license/ +-- + +-- ufal/clarin-dspace#1406 +-- +-- Metadata rows written before the 7.6.5 upgrade carry text_lang = '*' (Item.ANY), because the +-- language argument of addMetadata() was passed through verbatim. Since 7.6.5 +-- MetadataValue.setLanguage() normalizes '*' -> NULL on write, so this migration only repairs +-- pre-existing rows; no code path can create new ones. +-- +-- Stale '*' rows break the metadata-export -> metadata-import round trip (the export emits +-- rejected [*] headers) and make OAI store , from which oai_openaire emits an +-- invalid xml:lang="*". +-- +-- Only '*' is normalized. Empty-string languages are legal and round-trip correctly - leave them. + +-- Step 1 must run before step 2: it uses the '*' marker to find the affected items. +-- Bumping last_modified is what makes the reindex happen without a forced full rebuild: +-- `dspace oai import` selects items with last_modified > watermark, and `dspace index-discovery` +-- (no flags) reindexes documents whose search.lastindexed predates last_modified. +UPDATE item SET last_modified = CURRENT_TIMESTAMP + WHERE uuid IN (SELECT dspace_object_id FROM metadatavalue WHERE text_lang = '*'); + +-- Step 2 is deliberately not restricted to items. Rows on collections, communities and +-- bitstreams are normalized too and need no reindex: those tables have no last_modified column, +-- they are not in OAI or in metadata-export, and no indexing path reads the language of a +-- non-item DSO (SolrServiceMetadataBrowseIndexingPlugin bails on non-items, and the container +-- index factories never call getLanguage()). +UPDATE metadatavalue SET text_lang = NULL WHERE text_lang = '*'; diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2026.08.11__Normalize_metadata_language.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2026.08.11__Normalize_metadata_language.sql new file mode 100644 index 00000000000..66593f23601 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2026.08.11__Normalize_metadata_language.sql @@ -0,0 +1,34 @@ +-- +-- The contents of this file are subject to the license and copyright +-- detailed in the LICENSE and NOTICE files at the root of the source +-- tree and available online at +-- +-- http://www.dspace.org/license/ +-- + +-- ufal/clarin-dspace#1406 +-- +-- Metadata rows written before the 7.6.5 upgrade carry text_lang = '*' (Item.ANY), because the +-- language argument of addMetadata() was passed through verbatim. Since 7.6.5 +-- MetadataValue.setLanguage() normalizes '*' -> NULL on write, so this migration only repairs +-- pre-existing rows; no code path can create new ones. +-- +-- Stale '*' rows break the metadata-export -> metadata-import round trip (the export emits +-- rejected [*] headers) and make OAI store , from which oai_openaire emits an +-- invalid xml:lang="*". +-- +-- Only '*' is normalized. Empty-string languages are legal and round-trip correctly - leave them. + +-- Step 1 must run before step 2: it uses the '*' marker to find the affected items. +-- Bumping last_modified is what makes the reindex happen without a forced full rebuild: +-- `dspace oai import` selects items with last_modified > watermark, and `dspace index-discovery` +-- (no flags) reindexes documents whose search.lastindexed predates last_modified. +UPDATE item SET last_modified = CURRENT_TIMESTAMP + WHERE uuid IN (SELECT dspace_object_id FROM metadatavalue WHERE text_lang = '*'); + +-- Step 2 is deliberately not restricted to items. Rows on collections, communities and +-- bitstreams are normalized too and need no reindex: those tables have no last_modified column, +-- they are not in OAI or in metadata-export, and no indexing path reads the language of a +-- non-item DSO (SolrServiceMetadataBrowseIndexingPlugin bails on non-items, and the container +-- index factories never call getLanguage()). +UPDATE metadatavalue SET text_lang = NULL WHERE text_lang = '*';