/* * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Florent Guillaume */ package org.nuxeo.ecm.core.storage.sql.db; import java.io.Serializable; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.h2.tools.SimpleResultSet; /** * Functions used as stored procedures for H2. * * @author Florent Guillaume */ public class H2Functions extends EmbeddedFunctions { // for debug private static boolean isLogEnabled() { return false; // return log.isTraceEnabled(); } // for debug private static void logDebug(String message, Object... args) { // log.trace("SQL: " + String.format(message.replace("?", "%s"), args)); } public static boolean isInTreeString(Connection conn, String id, String baseId) throws SQLException { return isInTree(conn, id, baseId); } public static boolean isInTreeLong(Connection conn, Long id, Long baseId) throws SQLException { return isInTree(conn, id, baseId); } public static boolean isAccessAllowedString(Connection conn, String id, String principals, String permissions) throws SQLException { return isAccessAllowed(conn, id, split(principals), split(permissions)); } public static boolean isAccessAllowedLong(Connection conn, Long id, String principals, String permissions) throws SQLException { return isAccessAllowed(conn, id, split(principals), split(permissions)); } /** * Adds an invalidation from this cluster node to the invalidations list. */ @SuppressWarnings("boxing") public static void clusterInvalidateString(Connection conn, long nodeId, String id, String fragments, int kind) throws SQLException { PreparedStatement ps = null; try { // find other node ids String sql = "SELECT \"NODEID\" FROM \"CLUSTER_NODES\" WHERE \"NODEID\" <> ?"; if (isLogEnabled()) { logDebug(sql, nodeId); } ps = conn.prepareStatement(sql); ps.setLong(1, nodeId); List<Long> nodeIds = new LinkedList<Long>(); ResultSet rs = ps.executeQuery(); while (rs.next()) { nodeIds.add(rs.getLong(1)); } if (isLogEnabled()) { logDebug(" -> " + nodeIds); } // invalidate sql = "INSERT INTO \"CLUSTER_INVALS\" " + "(\"NODEID\", \"ID\", \"FRAGMENTS\", \"KIND\") " + "VALUES (?, ?, ?, ?)"; for (Long nid : nodeIds) { if (isLogEnabled()) { logDebug(sql, nid, id, fragments, kind); } ps = conn.prepareStatement(sql); ps.setLong(1, nid); ps.setObject(2, id); ps.setString(3, fragments); ps.setInt(4, kind); ps.execute(); } } finally { if (ps != null) { ps.close(); } } } /** * Gets the invalidations for this cluster node. * * @return a result set with columns id, fragments, kind */ public static ResultSet getClusterInvalidationsString(Connection conn, long nodeId) throws SQLException { DatabaseMetaData meta = conn.getMetaData(); SimpleResultSet result = new SimpleResultSet(); result.addColumn("ID", Types.VARCHAR, 0, 0); // String id result.addColumn("FRAGMENTS", Types.VARCHAR, 0, 0); result.addColumn("KIND", Types.INTEGER, 0, 0); if (meta.getURL().startsWith("jdbc:columnlist:")) { // this is just to query the result set columns return result; } PreparedStatement ps = null; try { String sql = "SELECT \"ID\", \"FRAGMENTS\", \"KIND\" FROM \"CLUSTER_INVALS\" " + "WHERE \"NODEID\" = ?"; if (isLogEnabled()) { logDebug(sql, nodeId); } ps = conn.prepareStatement(sql); ps.setLong(1, nodeId); ResultSet rs = ps.executeQuery(); List<Serializable> debugValues = null; if (isLogEnabled()) { debugValues = new LinkedList<Serializable>(); } while (rs.next()) { String id = rs.getString(1); String fragments = rs.getString(2); long kind = rs.getLong(3); result.addRow(new Object[] { id, fragments, Long.valueOf(kind) }); if (debugValues != null) { debugValues.add(id + ',' + fragments + ',' + kind); } } if (debugValues != null) { logDebug(" -> " + debugValues); } // remove processed invalidations sql = "DELETE FROM \"CLUSTER_INVALS\" WHERE \"NODEID\" = ?"; if (isLogEnabled()) { logDebug(sql); } ps.close(); ps = conn.prepareStatement(sql); ps.setLong(1, nodeId); ps.execute(); // return invalidations return result; } finally { if (ps != null) { ps.close(); } } } public static ResultSet upgradeVersions(Connection conn) throws SQLException { PreparedStatement ps1 = null; PreparedStatement ps2 = null; try { String sql = "SELECT v.id, v.versionableid, h.majorversion, h.minorversion" + " FROM versions v JOIN hierarchy h ON v.id = h.id" + " ORDER BY v.versionableid, v.created DESC"; ps1 = conn.prepareStatement(sql); ResultSet rs = ps1.executeQuery(); String series = null; boolean isLatest = false; boolean isLatestMajor = false; while (rs.next()) { String id = rs.getString("id"); String vid = rs.getString("versionableid"); long maj = rs.getLong("majorversion"); long min = rs.getLong("minorversion"); if (vid == null || !vid.equals(series)) { // restart isLatest = true; isLatestMajor = true; series = vid; } boolean isMajor = min == 0; ps2 = conn.prepareStatement("UPDATE versions SET label = ?, islatest = ?, islatestmajor = ?" + " WHERE id = ?"); ps2.setString(1, maj + "." + min); ps2.setBoolean(2, isLatest); ps2.setBoolean(3, isMajor && isLatestMajor); ps2.setString(4, id); ps2.executeUpdate(); // next isLatest = false; if (isMajor) { isLatestMajor = false; } } } finally { if (ps1 != null) { ps1.close(); } if (ps2 != null) { ps2.close(); } } return new SimpleResultSet(); } public static ResultSet upgradeLastContributor(Connection conn) throws SQLException { PreparedStatement ps1 = null; PreparedStatement ps2 = null; try { String sql = "SELECT dc_c.id, dc_c.item" + " FROM dublincore dc" + " JOIN (SELECT id, max(pos) AS pos FROM dc_contributors GROUP BY id) AS tmp ON (dc.id = tmp.id)" + " JOIN dc_contributors dc_c ON (tmp.id = dc_c.id AND tmp.pos = dc_c.pos)" + " WHERE dc.lastContributor IS NULL;"; ps1 = conn.prepareStatement(sql); ResultSet rs = ps1.executeQuery(); String series = null; while (rs.next()) { String id = rs.getString("id"); String lastContributor = rs.getString("item"); ps2 = conn.prepareStatement("UPDATE dublincore SET lastContributor = ? WHERE id = ?"); ps2.setString(1, lastContributor); ps2.setString(2, id); ps2.executeUpdate(); } } finally { if (ps1 != null) { ps1.close(); } if (ps2 != null) { ps2.close(); } } return new SimpleResultSet(); } public static ResultSet getAncestorsIds(Connection conn, String idsString) throws SQLException { Set<String> ids = split(idsString); DatabaseMetaData meta = conn.getMetaData(); SimpleResultSet result = new SimpleResultSet(); result.addColumn("ID", Types.VARCHAR, 0, 0); // String id if (meta.getURL().startsWith("jdbc:columnlist:")) { // this is just to query the result set columns return result; } PreparedStatement ps = null; try { LinkedList<String> todo = new LinkedList<String>(ids); Set<String> done = new HashSet<String>(); Set<String> res = new HashSet<String>(); while (!todo.isEmpty()) { done.addAll(todo); String sql = getSelectParentIdsByIdsSql(todo.size()); if (isLogEnabled()) { logDebug(sql, todo); } ps = conn.prepareStatement(sql); int i = 1; for (String id : todo) { ps.setString(i++, id); } todo = new LinkedList<String>(); List<String> debugIds = null; if (isLogEnabled()) { debugIds = new LinkedList<String>(); } ResultSet rs = ps.executeQuery(); while (rs.next()) { String id = rs.getString(1); if (id != null) { if (!res.contains(id)) { res.add(id); result.addRow(new Object[] { id }); } if (!done.contains(id)) { todo.add(id); } if (isLogEnabled()) { debugIds.add(id); } } } if (isLogEnabled()) { logDebug(" -> " + debugIds); } ps.close(); ps = null; } return result; } finally { if (ps != null) { ps.close(); } } } protected static String getSelectParentIdsByIdsSql(int size) { StringBuilder buf = new StringBuilder("SELECT DISTINCT \"PARENTID\" FROM \"HIERARCHY\" WHERE \"ID\" IN ("); for (int i = 0; i < size; i++) { if (i != 0) { buf.append(", "); } buf.append('?'); } buf.append(')'); return buf.toString(); } }