/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package com.xpn.xwiki.store.migration.hibernate; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.inject.Named; import javax.inject.Singleton; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.jdbc.Work; import org.xwiki.component.annotation.Component; import com.xpn.xwiki.XWiki; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.store.DatabaseProduct; import com.xpn.xwiki.store.XWikiHibernateBaseStore.HibernateCallback; import com.xpn.xwiki.store.migration.DataMigrationException; import com.xpn.xwiki.store.migration.XWikiDBVersion; import com.xpn.xwiki.web.Utils; /** * Migration for XWIKI-7564: Manually change the SQL type of long binary columns from LONG RAW to proper BLOBs when the * underlying database is Oracle. More specifically, this migrator changes the attachment content and attachment archive * columns from LONG RAW to BLOB, and rebuilds the indexes on those tables afterwards. The columns must be switched to * BLOB since this is the expected column type when using the new mapping files. Rebuilding the indexes is needed * because changing a table's columns automatically invalidates the indexes on those tables, and with unusable indexes * any new insertion in those tables will trigger an exception. * * @version $Id: 053efc77e5ebf708ad71f01c2e32c42fe4bc237d $ * @since 3.5.1 */ @Component @Named("R35101XWIKI7645") @Singleton public class R35101XWIKI7645DataMigration extends AbstractHibernateDataMigration { @Override public String getDescription() { return "See https://jira.xwiki.org/browse/XWIKI-7645"; } @Override public XWikiDBVersion getVersion() { return new XWikiDBVersion(35101); } @Override public boolean shouldExecute(XWikiDBVersion startupVersion) { boolean shouldExecute = false; try { getStore().beginTransaction(getXWikiContext()); // Run this migration if the database isn't new shouldExecute = getStore().getDatabaseProductName() == DatabaseProduct.ORACLE; getStore().endTransaction(getXWikiContext(), false); } catch (XWikiException ex) { // Shouldn't happen, ignore } catch (DataMigrationException ex) { // Shouldn't happen, ignore } return shouldExecute; } @Override public void hibernateMigrate() throws DataMigrationException, XWikiException { getStore().executeWrite(getXWikiContext(), new HibernateCallback<Object>() { @Override public Object doInHibernate(Session session) throws HibernateException, XWikiException { session.doWork(new R35101Work()); return Boolean.TRUE; } }); } /** * Hibernate {@link Work} class doing the actual work of this migrator. * * @version $Id: 053efc77e5ebf708ad71f01c2e32c42fe4bc237d $ */ private static class R35101Work implements Work { @Override public void execute(Connection connection) throws SQLException { String[][] tablesToFix = new String[][] { { "XWIKIATTACHMENT_CONTENT", "XWA_CONTENT" }, { "XWIKIATTACHMENT_ARCHIVE", "XWA_ARCHIVE" } }; Statement stmt = connection.createStatement(); PreparedStatement getIndexesQuery = connection.prepareStatement( "SELECT index_name FROM all_indexes WHERE table_owner=? AND table_name=? AND index_type='NORMAL'"); for (String[] table : tablesToFix) { try { stmt.execute("ALTER TABLE " + table[0] + " MODIFY (" + table[1] + " blob)"); } catch (SQLException ex) { // This exception is thrown when this migrator isn't really needed. This happens when migrating from // a version between 3.2 and 3.5, which do use the proper table structure, but we can't easily // distinguish between a pre-3.2 database and a post-3.2 database. // ORA-22859 on Oracle 11g, but ORA-22858 on Oracle 10g should be ignored. if (ex.getMessage().contains("ORA-22859") || ex.getMessage().contains("ORA-22858")) { return; } else { throw ex; } } getIndexesQuery.setString(1, getSchemaFromWikiName(Utils.getContext().getWikiId())); getIndexesQuery.setString(2, table[0]); ResultSet indexes = getIndexesQuery.executeQuery(); while (indexes.next()) { String index = indexes.getString(1); stmt.execute("ALTER INDEX " + index + " REBUILD"); } } } /** * The actual schema name isn't always the same as the virtual wiki name. Two settings in xwiki.cfg can change * this. For the main wiki, by default "xwiki" is used as the schema name, but the {@code xwiki.db} setting can * override this. Also, the {@code xwiki.db.prefix} can define a prefix that should be appended to all schema * names, so that all the wikis in a farm can have a common prefix. And on Oracle, the schema name is always * uppercased. * * @param wikiName the name of the virtual wiki for which to compute the schema name * @return the schema name corresponding to the virtual wiki, in UPPERCASE */ private String getSchemaFromWikiName(String wikiName) { if (wikiName == null) { return null; } XWikiContext context = Utils.getContext(); XWiki wiki = context.getWiki(); String schema; if (context.isMainWiki(wikiName)) { // Main wiki database, by default is "xwiki", but can be changed in xwiki.cfg schema = wiki.Param("xwiki.db"); if (schema == null) { schema = wikiName; } } else { // Virtual wiki database name is the name of the wiki schema = wikiName.replace('-', '_'); } // Apply an optional prefix defined in xwiki.cfg String prefix = wiki.Param("xwiki.db.prefix", ""); schema = prefix + schema; // Oracle schema names are UPPERCASE return schema.toUpperCase(); } } }