1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package com.xpn.xwiki.store.migration.hibernate

File R35102XWIKI7771DataMigration.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart1.png
82% of files have more coverage

Code metrics

6
42
7
2
195
125
13
0.31
6
3.5
1.86

Classes

Class Line # Actions
R35102XWIKI7771DataMigration 59 13 0% 7 16
0.1111111111.1%
R35102XWIKI7771DataMigration.R35102Work 112 29 0% 6 37
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20   
21    package com.xpn.xwiki.store.migration.hibernate;
22   
23    import java.sql.Connection;
24    import java.sql.PreparedStatement;
25    import java.sql.ResultSet;
26    import java.sql.SQLException;
27    import java.sql.Statement;
28    import java.text.MessageFormat;
29    import java.util.HashMap;
30    import java.util.Map;
31    import java.util.Map.Entry;
32   
33    import javax.inject.Named;
34    import javax.inject.Singleton;
35   
36    import org.apache.commons.lang3.StringUtils;
37    import org.hibernate.HibernateException;
38    import org.hibernate.Session;
39    import org.hibernate.jdbc.Work;
40    import org.slf4j.Logger;
41    import org.slf4j.LoggerFactory;
42    import org.xwiki.component.annotation.Component;
43   
44    import com.xpn.xwiki.XWikiException;
45    import com.xpn.xwiki.store.DatabaseProduct;
46    import com.xpn.xwiki.store.XWikiHibernateBaseStore.HibernateCallback;
47    import com.xpn.xwiki.store.migration.DataMigrationException;
48    import com.xpn.xwiki.store.migration.XWikiDBVersion;
49   
50    /**
51    * Migration for XWIKI-7771: Fix the LOBs wrongly created by the 3.2-3.5 mapping files for PostgreSQL.
52    *
53    * @version $Id: d06a53466464274a6450807604019d4dae074b16 $
54    * @since 3.5.1
55    */
56    @Component
57    @Named("R35102XWIKI7771")
58    @Singleton
 
59    public class R35102XWIKI7771DataMigration extends AbstractHibernateDataMigration
60    {
 
61  0 toggle @Override
62    public String getDescription()
63    {
64  0 return "See http://jira.xwiki.org/browse/XWIKI-7771";
65    }
66   
 
67  206 toggle @Override
68    public XWikiDBVersion getVersion()
69    {
70  206 return new XWikiDBVersion(35102);
71    }
72   
 
73  0 toggle @Override
74    public boolean shouldExecute(XWikiDBVersion startupVersion)
75    {
76  0 boolean shouldExecute = false;
77  0 try {
78  0 getStore().beginTransaction(getXWikiContext());
79    // Run this migration if the database isn't new
80  0 shouldExecute = (startupVersion.getVersion() > 0
81    && getStore().getDatabaseProductName() == DatabaseProduct.POSTGRESQL);
82  0 getStore().endTransaction(getXWikiContext(), false);
83    } catch (XWikiException ex) {
84    // Shouldn't happen, ignore
85    } catch (DataMigrationException ex) {
86    // Shouldn't happen, ignore
87    }
88  0 return shouldExecute;
89    }
90   
 
91  0 toggle @Override
92    public void hibernateMigrate() throws DataMigrationException, XWikiException
93    {
94  0 getStore().executeWrite(getXWikiContext(), new HibernateCallback<Object>()
95    {
 
96  0 toggle @Override
97    public Object doInHibernate(Session session) throws HibernateException, XWikiException
98    {
99  0 session.doWork(new R35102Work("xwikircs", "xwr_patch", "xwr_docid", "History for document with ID"));
100  0 session.doWork(new R35102Work("xwikirecyclebin", "xdd_xml", "xdd_id", "Deleted document with ID"));
101  0 session.doWork(new R35102Work("xwikiattrecyclebin", "xda_xml", "xda_id", "Deleted attachment with ID"));
102  0 return Boolean.TRUE;
103    }
104    });
105    }
106   
107    /**
108    * Hibernate {@link Work} class reads back LOB data into Text fields.
109    *
110    * @version $Id: d06a53466464274a6450807604019d4dae074b16 $
111    */
 
112    private static class R35102Work implements Work
113    {
114    /** The name of the table to fix. */
115    private String tableName;
116   
117    /** The name of the column to fix. */
118    private String columnName;
119   
120    /** The name of the column holding an identifier which can be printed in the logs when a migration fails. */
121    private String idColumnName;
122   
123    /** What kind of data is corrupt when a migration fails. */
124    private String dataType;
125   
126    /** Logging helper object. */
127    private Logger logger = LoggerFactory.getLogger(R35102Work.class);
128   
129    /**
130    * Constructor specifying the table and columns to fix.
131    *
132    * @param tableName the name of the table to fix
133    * @param columnName the name of the column to fix
134    * @param idColumnName the name of the column containing an identifier that should be printed in the logs when a
135    * migration fails
136    * @param dataType the data type printed in the logs when a migration fails
137    */
 
138  0 toggle R35102Work(String tableName, String columnName, String idColumnName, String dataType)
139    {
140  0 this.tableName = tableName;
141  0 this.columnName = columnName;
142  0 this.idColumnName = idColumnName;
143  0 this.dataType = dataType;
144    }
145   
 
146  0 toggle @Override
147    public void execute(Connection connection) throws SQLException
148    {
149  0 Statement stmt = connection.createStatement();
150  0 ResultSet lobs = stmt.executeQuery("SELECT " + this.columnName + ", " + this.idColumnName + " FROM "
151    + this.tableName + ";");
152  0 Map<String, Long> lobsToProcess = new HashMap<String, Long>();
153  0 while (lobs.next()) {
154    // If we're not migrating data created by a version between 3.2 and 3.5, then the data is already OK
155  0 if (StringUtils.isNumeric(lobs.getString(1))) {
156  0 lobsToProcess.put(lobs.getString(1), lobs.getLong(2));
157    }
158    }
159  0 PreparedStatement inlineLob = connection.prepareStatement(MessageFormat.format("UPDATE {0} SET {1} ="
160    + " convert_from(loread(lo_open({1}::int, 262144), 10000000), ''LATIN1'') WHERE {1} = ?",
161    this.tableName, this.columnName));
162  0 PreparedStatement emptyLob = connection.prepareStatement(MessageFormat.format("UPDATE {0} SET {1} = ''''"
163    + " WHERE {1} = ?", this.tableName, this.columnName));
164  0 PreparedStatement removeLob = connection.prepareStatement("select lo_unlink(?)");
165  0 for (Entry<String, Long> lob : lobsToProcess.entrySet()) {
166  0 try {
167  0 inlineLob.setString(1, lob.getKey());
168  0 inlineLob.executeUpdate();
169    // We commit early since any error will invalidate the whole transaction
170  0 removeLob.setLong(1, Long.valueOf(lob.getKey()));
171  0 removeLob.execute();
172  0 connection.commit();
173    } catch (SQLException ex) {
174  0 if (ex.getMessage().contains("0x00")) {
175    // The hibernate mapping file was broken between 3.2 and 3.5 for PostgreSQL, and any non-ASCII
176    // characters written to the database got broken since for each character, only the last 8 bits
177    // of the character's unicode value was sent to the database. There's no way of getting back the
178    // missing bytes, we can just empty the value set in this row.
179    // Start a new transaction
180  0 connection.rollback();
181  0 this.logger.warn(this.dataType + " [{}] cannot be recovered",
182    lob.getValue());
183  0 emptyLob.setString(1, lob.getKey());
184  0 emptyLob.executeUpdate();
185  0 removeLob.setLong(1, Long.valueOf(lob.getKey()));
186  0 removeLob.execute();
187  0 connection.commit();
188    } else {
189  0 throw ex;
190    }
191    }
192    }
193    }
194    }
195    }