/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Florent Guillaume
*/
package org.eclipse.ecr.core.storage.sql.extensions;
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.LinkedList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.h2.tools.SimpleResultSet;
/**
* Functions used as stored procedures for H2.
*
* @author Florent Guillaume
*/
public class H2Functions extends EmbeddedFunctions {
private static final Log log = LogFactory.getLog(H2Functions.class);
// 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, 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\" <> SESSION_ID()";
if (isLogEnabled()) {
logDebug(sql);
}
ps = conn.prepareStatement(sql);
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 nodeId : nodeIds) {
if (isLogEnabled()) {
logDebug(sql, nodeId, id, fragments, kind);
}
ps = conn.prepareStatement(sql);
ps.setLong(1, nodeId);
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)
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;
Statement st = null;
try {
String sql = "SELECT \"ID\", \"FRAGMENTS\", \"KIND\" FROM \"CLUSTER_INVALS\" "
+ "WHERE \"NODEID\" = SESSION_ID()";
if (isLogEnabled()) {
logDebug(sql);
}
ps = conn.prepareStatement(sql);
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\" = SESSION_ID()";
if (isLogEnabled()) {
logDebug(sql);
}
st = conn.createStatement();
st.execute(sql);
// return invalidations
return result;
} finally {
if (ps != null) {
ps.close();
}
if (st != null) {
st.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();
}
}