/*
* This file or a portion of this file is licensed under the terms of
* the Globus Toolkit Public License, found in file ../GTPL, or at
* http://www.globus.org/toolkit/download/license.html. This notice must
* appear in redistributions of this file, with or without modification.
*
* Redistributions of this Software, with or without modification, must
* reproduce the GTPL in: (1) the Software, or (2) the Documentation or
* some other similar material which is provided with the Software (if
* any).
*
* Copyright 1999-2004 University of Chicago and The University of
* Southern California. All rights reserved.
*/
package org.griphyn.vdl.dbschema;
import java.sql.*;
import java.util.*;
import java.io.*;
import java.lang.reflect.*;
import java.net.InetAddress;
import org.griphyn.vdl.util.ChimeraProperties;
import org.griphyn.vdl.classes.*;
import org.griphyn.vdl.util.Logging;
import org.griphyn.vdl.parser.*;
import org.griphyn.vdl.router.Cache;
import org.xml.sax.InputSource;
/**
* This class provides basic functionalities to interact with the
* backend database, such as insertion, deletion, and search of
* entities in the VDC.
*
* @author Jens-S. Vöckler
* @author Yong Zhao
* @version $Revision$
*/
public class ChunkSchema extends DatabaseSchema implements VDC
{
/**
* Name of the four parameter tables in human readable format.
*/
protected static final String[] c_lfn_names =
{ "VDC_NLFN", "VDC_ILFN", "VDC_OLFN", "VDC_BLFN" };
/**
* Communication between saveDefinition and deleteDefinition in
* update mode.
*/
protected boolean m_deferDeleteCommit;
/**
* An instance of the VDLx XML parser.
*/
private org.griphyn.vdl.parser.VDLxParser m_parser;
/**
* A cache for definitions to avoid reloading from the database.
*/
protected Cache m_cache;
/**
* Instantiates an XML parser for VDLx on demand. Since XML parsing
* XML parsing and parser instantiation is an expensive business, the
* reader will only be generated on demand.
*
* @return a valid VDLx parser instance.
*/
private org.griphyn.vdl.parser.VDLxParser parserInstance()
{
if ( this.m_parser == null ) {
// obtain the schema location URL from the schema properties:
// url is a list of strings representing schema locations. The
// content exists in pairs, one of the namespace URI, one of the
// location URL.
String url = null;
try {
ChimeraProperties props = ChimeraProperties.instance();
url =
m_dbschemaprops.getProperty( "xml.url", props.getVDLSchemaLocation() );
} catch (IOException e) {
Logging.instance().log("chunk", 0, "ignored " + e);
}
this.m_parser = new org.griphyn.vdl.parser.VDLxParser(url);
}
// done
return this.m_parser;
}
/**
* Default constructor for the "chunk" schema.
*
* @param dbDriverName is the database driver name
*/
public ChunkSchema( String dbDriverName )
throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException,
SQLException, IOException
{
// load the driver from the properties
super( dbDriverName, PROPERTY_PREFIX );
Logging.instance().log( "dbschema", 3, "done with default schema c'tor" );
this.m_cache = this.m_dbdriver.cachingMakesSense() ? new Cache(600) : null;
this.m_deferDeleteCommit = false;
this.m_parser = null;
this.m_dbdriver.insertPreparedStatement( "stmt.save.definition",
"INSERT INTO vdc_definition(id,type,name,namespace,version,xml) " +
"VALUES (?,?,?,?,?,?)" );
this.m_dbdriver.insertPreparedStatement( "stmt.save.nlfn",
"INSERT INTO vdc_nlfn(id,name) VALUES (?,?)" );
this.m_dbdriver.insertPreparedStatement( "stmt.save.ilfn",
"INSERT INTO vdc_ilfn(id,name) VALUES (?,?)" );
this.m_dbdriver.insertPreparedStatement( "stmt.save.olfn",
"INSERT INTO vdc_olfn(id,name) VALUES (?,?)" );
this.m_dbdriver.insertPreparedStatement( "stmt.save.blfn",
"INSERT INTO vdc_blfn(id,name) VALUES (?,?)" );
this.m_dbdriver.insertPreparedStatement( "stmt.select.nlfn",
"SELECT distinct id FROM vdc_nlfn WHERE name=?" );
this.m_dbdriver.insertPreparedStatement( "stmt.select.ilfn",
"SELECT distinct id FROM vdc_ilfn WHERE name=?" );
this.m_dbdriver.insertPreparedStatement( "stmt.select.olfn",
"SELECT distinct id FROM vdc_olfn WHERE name=?" );
this.m_dbdriver.insertPreparedStatement( "stmt.select.blfn",
"SELECT distinct id FROM vdc_blfn WHERE name=?" );
this.m_dbdriver.insertPreparedStatement( "stmt.select.all.lfn",
"SELECT distinct did FROM vdc_nlfn WHERE name=? UNION " +
"SELECT distinct did FROM vdc_ilfn WHERE name=? UNION " +
"SELECT distinct did FROM vdc_olfn WHERE name=? UNION " +
"SELECT distinct did FROM vdc_blfn WHERE name=?" );
this.m_dbdriver.insertPreparedStatement( "stmt.select.xml.id",
"SELECT xml FROM vdc_definition WHERE id=?" );
this.m_dbdriver.insertPreparedStatement( "stmt.select.xml",
"SELECT id,xml FROM vdc_definition WHERE type=? AND name=? AND namespace=? AND version=?" );
this.m_dbdriver.insertPreparedStatement( "stmt.select.id",
"SELECT id FROM vdc_definition WHERE type=? AND name=? AND namespace=? AND version=?" );
this.m_dbdriver.insertPreparedStatement( "stmt.delete.xml",
"DELETE FROM vdc_definition WHERE id=?" );
this.m_dbdriver.insertPreparedStatement( "stmt.delete.nlfn",
"DELETE FROM vdc_nlfn WHERE id=?" );
this.m_dbdriver.insertPreparedStatement( "stmt.delete.ilfn",
"DELETE FROM vdc_ilfn WHERE id=?" );
this.m_dbdriver.insertPreparedStatement( "stmt.delete.olfn",
"DELETE FROM vdc_olfn WHERE id=?" );
this.m_dbdriver.insertPreparedStatement( "stmt.delete.blfn",
"DELETE FROM vdc_blfn WHERE id=?" );
}
//
// lower level methods, working directly on specific definitions
//
/**
* Loads a single Definition from the backend database into an Java object.
* This method does not allow wildcarding!
*
* @param namespace namespace, null will be converted into empty string
* @param name name, null will be converted into empty string
* @param version version, null will be converted into empty string
* @param type type of the definition (TR or DV), must not be -1.
* @return the Definition as specified, or null if not found.
*
* @see org.griphyn.vdl.classes.Definition#TRANSFORMATION
* @see org.griphyn.vdl.classes.Definition#DERIVATION
* @see #saveDefinition( Definition, boolean )
*/
public Definition
loadDefinition( String namespace,
String name,
String version,
int type )
throws SQLException
{
Definition result = null;
Logging.instance().log("xaction", 1, "START load definition" );
int i = 1;
PreparedStatement ps = m_dbdriver.getPreparedStatement( "stmt.select.xml");
ps.setInt( i++, type );
ps.setString( i++, makeNotNull(name) );
ps.setString( i++, makeNotNull(namespace) );
ps.setString( i++, makeNotNull(version) );
Logging.instance().log( "chunk", 2, "SELECT xml FROM definition" );
ResultSet rs = ps.executeQuery();
Logging.instance().log("xaction", 1, "INTER load definition" );
if ( rs.next() ) {
MyCallbackHandler cb = new MyCallbackHandler();
Long lid = new Long( rs.getLong("id") );
// FIXME: multiple null handlings missing
parserInstance().parse(
new org.xml.sax.InputSource(rs.getCharacterStream("xml")), cb );
result = cb.getDefinition();
// add to cache
if ( m_cache != null ) m_cache.set( lid, result );
} else {
Logging.instance().log( "chunk", 0, "Definition not found" );
}
rs.close();
Logging.instance().log("xaction", 1, "FINAL load definition" );
return result;
}
/**
* Load a single Definition from the backend database into a Java
* object by its primary key id. This is an internal helper function.
*
* @param id is a long which represent the primary id.
* @return the Definitions that was matched by the id.
*
* @see #loadDefinition( String, String, String, int )
* @see #saveDefinition( Definition, boolean )
*/
private Definition loadDefinition( long id )
throws SQLException
{
Definition result = null;
Long lid = new Long(id);
Logging.instance().log("xaction", 1, "START load definition " + lid );
// try grabbing from cache
if ( m_cache != null )
result = (Definition) m_cache.get(lid);
if ( result == null ) {
// no cache, or not in cache
PreparedStatement ps = m_dbdriver.getPreparedStatement( "stmt.select.xml.id" );
if ( m_dbdriver.preferString() ) ps.setString( 1, Long.toString(id) );
else ps.setLong( 1, id );
ResultSet rs = ps.executeQuery();
Logging.instance().log("xaction", 1, "INTER load definitions" );
if ( rs.next() ) {
MyCallbackHandler cb = new MyCallbackHandler();
// FIXME: multiple null handlings missing
parserInstance().parse(
new org.xml.sax.InputSource(rs.getCharacterStream("xml")), cb );
result = cb.getDefinition();
// add to cache
if ( m_cache != null ) m_cache.set( lid, result );
} else {
Logging.instance().log( "chunk", 0, "Definition not found" );
}
rs.close();
}
Logging.instance().log("xaction", 1, "FINAL load definitions" );
return result;
}
/**
* Compiles the name of a DV/TR for log messages.
*
* @param d is a definition
* @return the type plus FQDN of the definition
*/
private String what( Definition d )
{
StringBuffer result = new StringBuffer();
switch ( d.getType() ) {
case Definition.DERIVATION:
result.append( "DV" );
break;
case Definition.TRANSFORMATION:
result.append( "TR" );
break;
default:
result.append( "??" );
break;
}
result.append(' ').append( d.shortID() );
return result.toString();
}
/**
* Saves a Definition, that is either a Transformation or Derivation,
* into the backend database. This method, of course, does not allow
* wildcarding. The definition has to be completely specified and
* valid.
*
* @param definition is the new Definition to store.
* @param overwrite true, if existing defitions will be overwritten by
* new ones with the same primary (or secondary) key (-set), or false,
* if a new definition will be rejected on key matches.
*
* @return true, if the backend database was changed, or
* false, if the definition was not accepted into the backend.
*
* @see org.griphyn.vdl.classes.Definition
* @see org.griphyn.vdl.classes.Transformation
* @see org.griphyn.vdl.classes.Derivation
* @see #loadDefinition( String, String, String, int )
*/
public boolean
saveDefinition( Definition definition,
boolean overwrite )
throws SQLException
{
Logging.instance().log( "chunk", 2, "SAVE DEFINITION started" );
// figure out, if it already exists
long probe = -1;
try {
Long temp = getDefinitionId(definition);
if ( temp != null ) probe = temp.longValue();
} catch ( SQLException e ) {
String cause = e.getMessage();
Logging.instance().log( "app", 1, "Ignoring SQL exception" +
( cause==null ? "" : ": " + cause ) );
m_dbdriver.clearWarnings();
}
if ( probe != -1 ) {
if ( overwrite ) {
// in overwrite mode, remove old version
Logging.instance().log( "app", 1, "Deleting old " +
definition.shortID() );
// remove old definition from database (delete-before-insert)
try {
this.m_deferDeleteCommit = true;
deleteDefinition( definition );
} catch ( SQLException e ) {
String cause = e.getMessage();
Logging.instance().log( "app", 1, "Ignoring SQL exception" +
( cause==null ? "" : ": " + e.getMessage() ) );
} finally {
this.m_deferDeleteCommit = false;
}
} else {
// not overwriting, tell user
Logging.instance().log( "app", 0, definition.shortID() +
" already exists (SQL vdc_definition.id=" +
probe + "), ignoring" );
return false;
}
}
// Definition is prestine (now)
Logging.instance().log( "app", 1, "Trying to add " + what(definition) );
long id = -1;
try {
id = m_dbdriver.sequence1( "def_id_seq" );
} catch ( SQLException e ) {
Logging.instance().log( "app", 0, "In " + definition.shortID() + ": " +
e.toString().trim() );
Logging.instance().log("xaction", 1, "START rollback" );
m_dbdriver.cancelPreparedStatement( "stmt.save.definition" );
m_dbdriver.rollback();
Logging.instance().log("xaction", 1, "FINAL rollback" );
return false;
}
// add ID explicitely from sequence to insertion -- -1 is autoinc
Logging.instance().log("xaction", 1, "START save definition" );
PreparedStatement ps = m_dbdriver.getPreparedStatement("stmt.save.definition");
int i = 1;
longOrNull( ps, i++, id );
ps.setInt( i++, definition.getType() );
if ( definition.getName() == null )
throw new SQLException( "VDS inconsistency: The name of a definition is null" );
else
ps.setString( i++, definition.getName() );
ps.setString( i++, makeNotNull(definition.getNamespace()) );
ps.setString( i++, makeNotNull(definition.getVersion()) );
String xml = definition.toXML( (String) null, (String) null );
ps.setCharacterStream( i++, new StringReader(xml), xml.length() );
// save prepared values
Logging.instance().log( "chunk", 2, "INSERT INTO Definition" );
try {
ps.executeUpdate();
if ( id == -1 ) id = m_dbdriver.sequence2( ps, "def_id_seq", 1 );
} catch ( SQLException e ) {
Logging.instance().log( "app", 0, "In " + definition.shortID() + ": " +
e.toString().trim() );
Logging.instance().log("xaction", 1, "START rollback" );
m_dbdriver.cancelPreparedStatement( "stmt.save.definition" );
m_dbdriver.rollback();
Logging.instance().log("xaction", 1, "FINAL rollback" );
return false;
}
Logging.instance().log("xaction", 1, "FINAL save definition: ID=" + id );
/* NOT YET
*
// add to cache
if ( m_cache != null ) m_cache.set( new Long(id), definition );
*
*/
// batch save LFNs from Derivations
if ( definition instanceof Derivation ) {
Derivation derivation = (Derivation) definition;
Set alreadyKnown = new HashSet();
// ordering MUST MATCH classes.LFN constants!
PreparedStatement stmt[] = {
m_dbdriver.getPreparedStatement("stmt.save.nlfn"),
m_dbdriver.getPreparedStatement("stmt.save.ilfn"),
m_dbdriver.getPreparedStatement("stmt.save.olfn"),
m_dbdriver.getPreparedStatement("stmt.save.blfn") };
int[] count = new int[ stmt.length ];
for ( int ii=0; ii<count.length; ++ii ) count[ii] = 0;
for ( Iterator j=derivation.iteratePass(); j.hasNext(); ) {
Value value = ((Pass) j.next()).getValue();
if ( value != null ) {
switch ( value.getContainerType() ) {
case Value.SCALAR:
// check Scalar contents for LFN
saveScalar( id, (Scalar) value, alreadyKnown, stmt, count );
break;
case Value.LIST:
// check List for Scalars for LFN
for ( Iterator k=((org.griphyn.vdl.classes.List)value).iterateScalar(); k.hasNext(); ) {
saveScalar( id, (Scalar) k.next(), alreadyKnown, stmt, count );
}
break;
default:
throw new RuntimeException( "unknown container type" );
}
}
}
for ( int ii=0; ii<stmt.length; ++ii ) {
// anything to do?
if ( count[ii] > 0 ) {
// batch insert
Logging.instance().log( "chunk", 2, "BATCH INSERT for " + count[ii] +
' ' + c_lfn_names[ii] + 's' );
Logging.instance().log( "xaction", 1, "START batch-add " + count[ii]
+ ' ' + c_lfn_names[ii] );
int[] update = stmt[ii].executeBatch();
Logging.instance().log( "xaction", 1, "FINAL batch-add " + count[ii]
+ ' ' + c_lfn_names[ii] );
}
}
}
// commit the changes
Logging.instance().log("xaction", 1, "START commit" );
this.m_dbdriver.commit();
Logging.instance().log("xaction", 1, "FINAL commit" );
// done
return true;
}
/**
* Saves all logical filenames from a Scalar object. This is a helper
* function to save a single definition.
*
* @param id is the definition id in the DEFINITION table
* @param scalar is a Scalar instance of which the LFNs are to be saved.
* @param already is a set of filenames that were already added during
* this session
* @param stmt is an array of the ids of the prepared statements for
* the different tables.
* @param count count the number of entries in a prepared statement.
*
* @see #saveDefinition( Definition, boolean )
*/
private void saveScalar( long id, Scalar scalar, Set already,
PreparedStatement[] stmt, int[] count )
throws SQLException
{
int result = 0;
for ( Iterator i=scalar.iterateLeaf(); i.hasNext(); ) {
Leaf leaf = (Leaf) i.next();
// only interested in logical filenames, nothing else
if ( leaf instanceof LFN ) {
LFN lfn = (LFN) leaf;
String name = lfn.getFilename();
// already inserted previously?
if ( already.contains(name) ) continue;
else already.add(name);
// which one to chose
int link = lfn.getLink();
if ( ! LFN.isInRange(link) )
throw new RuntimeException( "unknown LFN linkage type" );
int n = 1;
if ( m_dbdriver.preferString() ) stmt[link].setString( n++, Long.toString(id) );
else stmt[link].setLong( n++, id );
stmt[link].setString( n++, name );
// only keep filenames and linkage in ancillary tables
// stringOrNull( stmt[link], n++, lfn.getTemporary() );
// FIXME: dontTransfer, dontRegister?
stmt[link].addBatch();
count[link]++;
}
}
}
//
// higher level methods, allowing for wildcarding unless working on
// a single Definition.
//
/**
* Obtains the primary key id for a given definition. "Fake" definitions
* are permissable. This is an internal helper function.
*
* @param d is a definition specification.
* @return the id of the definition, or null if not found.
*
* @see #getDefinitionId( String, String, String, int )
*/
protected Long getDefinitionId( Definition d )
throws SQLException
{
Logging.instance().log("xaction", 1, "START select ID from DEFINITION" );
Long result = null;
// ps.resetPreparedStatement( "stmt.select.id" );
int i = 1;
PreparedStatement ps = m_dbdriver.getPreparedStatement( "stmt.select.id");
ps.setInt( i++, d.getType() );
ps.setString( i++, makeNotNull(d.getName()) );
ps.setString( i++, makeNotNull(d.getNamespace()) );
ps.setString( i++, makeNotNull(d.getVersion()) );
Logging.instance().log( "chunk", 2, "SELECT id FROM definition" );
ResultSet rs = ps.executeQuery();
Logging.instance().log("xaction", 1, "INTER select ID from DEFINITION" );
if ( rs.next() ) result = new Long( rs.getLong(1) );
else Logging.instance().log( "chunk", 0, "Definition not found" );
rs.close();
Logging.instance().log("xaction", 1, "FINAL select ID from DEFINITION" );
return result;
}
/**
* Obtains the list of primary key ids for a matching definitions.
* This method allows for wildcards in the usual fashion. Use null for
* strings as wildcards, and -1 for the type wildcard. This method may
* return an empty list, but it will not return null. This is an
* internal helper function.
*
* @param namespace namespace, null to match any namespace
* @param name name, null to match any name
* @param version version, null to match any version
* @param type definition type (TR or DV)
* @return a possibly empty list containing all matching
* definition ids as Longs.
*
* @see org.griphyn.vdl.classes.Definition#TRANSFORMATION
* @see org.griphyn.vdl.classes.Definition#DERIVATION
* @see #getDefinitionId( Definition )
*/
protected java.util.List getDefinitionId( String namespace,
String name,
String version,
int type )
throws SQLException
{
java.util.List result = new ArrayList();
Logging.instance().log("xaction", 1, "START select IDs from DEFINITION" );
java.util.List select = new ArrayList(1);
select.add( new String("distinct id") );
java.util.Map where = new TreeMap();
if ( type != -1 ) where.put( "type", Integer.toString(type) );
if ( namespace != null ) where.put( "namespace", namespace );
if ( name != null ) where.put( "name", name );
if ( version != null ) where.put( "version", version );
Logging.instance().log("xaction", 1, "START select IDs" );
ResultSet rs = m_dbdriver.select(select,"vdc_definition",where,null);
while ( rs.next() )
result.add( new Long( rs.getLong("id") ) );
rs.close();
Logging.instance().log("xaction", 1, "FINAL select IDs from DEFINITION" );
return result;
}
/**
* Search the database for the existence of a definition.
*
* @param definition the definition object to search for
* @return true, if the definition exists, false if not found
*/
public boolean containsDefinition( Definition definition )
throws SQLException
{
boolean result = false;
try {
result = ( getDefinitionId(definition) != null );
} catch ( SQLException sql ) {
// ignore
}
return result;
}
/**
* Delete a specific Definition objects from the database. No wildcard
* matching will be done. "Fake" definitions are permissable, meaning
* it just has the secondary key triple.
*
* @param definition is the definition specification to delete
* @return true is something was deleted, false if non existent.
*
* @see org.griphyn.vdl.classes.Definition#TRANSFORMATION
* @see org.griphyn.vdl.classes.Definition#DERIVATION
*/
public boolean deleteDefinition( Definition definition )
throws SQLException
{
int result = 0;
PreparedStatement ps = null;
//
// TODO: turn into a stored procedure call
//
Logging.instance().log("xaction", 1, "START delete definition" );
Long defId = getDefinitionId(definition);
boolean preferString = m_dbdriver.preferString();
if ( defId != null ) {
long id = defId.longValue();
Logging.instance().log( "xaction", 1, "START DELETE FROM nlfn" );
ps = m_dbdriver.getPreparedStatement("stmt.delete.nlfn");
if ( preferString ) ps.setString( 1, Long.toString(id) );
else ps.setLong( 1, id );
result = ps.executeUpdate();
Logging.instance().log( "xaction", 1, "FINAL DELETE FROM nlfn: " + result );
Logging.instance().log( "xaction", 1, "START DELETE FROM ilfn" );
ps = m_dbdriver.getPreparedStatement( "stmt.delete.ilfn" );
if ( preferString ) ps.setString( 1, Long.toString(id) );
else ps.setLong( 1, id );
result = ps.executeUpdate();
Logging.instance().log( "xaction", 1, "FINAL DELETE FROM ilfn: " + result );
Logging.instance().log( "xaction", 1, "START DELETE FROM olfn" );
ps = m_dbdriver.getPreparedStatement( "stmt.delete.olfn" );
if ( preferString ) ps.setString( 1, Long.toString(id) );
else ps.setLong( 1, id );
result = ps.executeUpdate();
Logging.instance().log( "xaction", 1, "FINAL DELETE FROM olfn: " + result );
Logging.instance().log( "xaction", 1, "START DELETE FROM blfn" );
ps = m_dbdriver.getPreparedStatement( "stmt.delete.blfn" );
if ( preferString ) ps.setString( 1, Long.toString(id) );
else ps.setLong( 1, id );
result = ps.executeUpdate();
Logging.instance().log( "xaction", 1, "FINAL DELETE FROM blfn: " + result );
Logging.instance().log( "xaction", 1, "START DELETE FROM definition" );
ps = m_dbdriver.getPreparedStatement( "stmt.delete.xml" );
if ( preferString ) ps.setString( 1, Long.toString(id) );
else ps.setLong( 1, id );
result = ps.executeUpdate();
Logging.instance().log( "xaction", 1, "FINAL DELETE FROM definition: " + result );
if ( ! m_deferDeleteCommit ) m_dbdriver.commit();
}
Logging.instance().log("xaction", 1, "FINAL delete definition" );
return ( result != 0 );
}
/**
* Delete Definition objects from the database. This method allows for
* wildcards in the usual fashion. Use null for strings as wildcards,
* and -1 for the type wildcard. For efficiency reasons, this method
* will return an empty list.
*
* @param namespace namespace, null to match any namespace
* @param name name, null to match any name
* @param version version, null to match any version
* @param type definition type (TR or DV)
* @return a list containing all Definitions that were deleted
*
* @see org.griphyn.vdl.classes.Definition#TRANSFORMATION
* @see org.griphyn.vdl.classes.Definition#DERIVATION
*/
public java.util.List deleteDefinition( String namespace,
String name,
String version,
int type )
throws SQLException
{
java.util.List result = new ArrayList();
Logging.instance().log("xaction", 1, "START delete definitions" );
java.util.List idlist = getDefinitionId( namespace, name, version, type );
if ( idlist.size() == 0 ) return result;
// postcondition: contains all IDs, count(id)>0, to be deleted
// save old values
if ( ! m_deferDeleteCommit ) {
// we come from saveDefinition, thus we won't need saved values
for ( Iterator i=idlist.iterator(); i.hasNext(); ) {
Definition d = loadDefinition( ((Long) i.next()).longValue() );
if ( d != null ) result.add(d);
}
}
// list of all statements we need to access
PreparedStatement ps[] = {
this.m_dbdriver.getPreparedStatement("stmt.delete.nlfn"),
this.m_dbdriver.getPreparedStatement("stmt.delete.ilfn"),
this.m_dbdriver.getPreparedStatement("stmt.delete.olfn"),
this.m_dbdriver.getPreparedStatement("stmt.delete.blfn"),
this.m_dbdriver.getPreparedStatement("stmt.delete.xml")
};
// prepare and batch all statements
boolean preferString = m_dbdriver.preferString();
for ( Iterator i=idlist.iterator(); i.hasNext(); ) {
long id = ((Long) i.next()).longValue();
for ( int j=0; j<ps.length; ++j ) {
if ( preferString ) ps[j].setString( 1, Long.toString(id) );
else ps[j].setLong( 1, id );
ps[j].addBatch();
}
}
// run all batches
Logging.instance().log("xaction", 1, "INTER delete definitions" );
for ( int j=0; j<ps.length; ++j ) {
int[] status = new int[ idlist.size() ];
try {
status = ps[j].executeBatch();
} catch ( NullPointerException npe ) {
Logging.instance().log( "app", 1, "tripped over NPE, ignoring!" );
}
}
Logging.instance().log("xaction", 1, "FINAL delete definitions" );
if ( ! m_deferDeleteCommit ) m_dbdriver.commit();
return result;
}
/**
* Search the database for definitions by ns::name:version triple
* and by type (either Transformation or Derivation). This version
* of the search allows for jokers expressed as null value
*
* @param namespace namespace, null to match any namespace
* @param name name, null to match any name
* @param version version, null to match any version
* @param type type of definition (TR/DV, or both)
* @return a list of definitions
*
* @see org.griphyn.vdl.classes.Definition#TRANSFORMATION
* @see org.griphyn.vdl.classes.Definition#DERIVATION
*/
public java.util.List searchDefinition( String namespace,
String name,
String version,
int type )
throws SQLException
{
java.util.List idlist = getDefinitionId( namespace, name, version, type );
// TODO: make this a batch or sproc
java.util.List result = new ArrayList();
for ( Iterator i=idlist.iterator(); i.hasNext(); ) {
Definition d = loadDefinition( ((Long) i.next()).longValue() );
if ( d != null ) result.add(d);
}
return result;
}
/**
* Searches the database for all derivations that contain a certain LFN.
* The linkage is an additional constraint. This method does not allow
* jokers.
*
* @param lfn the LFN name
* @param link the linkage type of the LFN
* @return a list of Definition items that match the criterion.
*
* @see org.griphyn.vdl.classes.LFN#NONE
* @see org.griphyn.vdl.classes.LFN#INPUT
* @see org.griphyn.vdl.classes.LFN#OUTPUT
* @see org.griphyn.vdl.classes.LFN#INOUT
*/
public java.util.List searchFilename( String lfn, int link )
throws SQLException
{
if ( lfn == null )
throw new NullPointerException("You must query for a filename");
PreparedStatement ps = null;
if ( link == -1 ) {
// wildcard match
ps = this.m_dbdriver.getPreparedStatement( "stmt.select.all.lfn" );
for ( int ii=0; ii<c_lfn_names.length; ++ii ) ps.setString( ii+1, lfn );
Logging.instance().log( "chunk", 2, "SELECT distinct id FROM all lfn" +
" WHERE name='" + lfn + "'" );
} else if ( LFN.isInRange(link) ) {
// known linkage, one table only
switch ( link ) {
case LFN.NONE:
ps = this.m_dbdriver.getPreparedStatement("stmt.select.nlfn");
break;
case LFN.INPUT:
ps = this.m_dbdriver.getPreparedStatement("stmt.select.ilfn");
break;
case LFN.OUTPUT:
ps = this.m_dbdriver.getPreparedStatement("stmt.select.olfn");
break;
case LFN.INOUT:
ps = this.m_dbdriver.getPreparedStatement("stmt.select.blfn");
break;
};
ps.setString( 1, lfn );
Logging.instance().log( "chunk", 2, "SELECT distinct id FROM " +
c_lfn_names[link] + " WHERE name='" + lfn + "'" );
} else {
throw new RuntimeException( "Unknown linkage value " + link );
}
ResultSet rs = ps.executeQuery();
// TODO: make this a batch or sproc
java.util.List result = new ArrayList();
while ( rs.next() ) {
Definition d = loadDefinition( rs.getLong(1) );
if ( d != null ) result.add(d);
}
rs.close();
Logging.instance().log("xaction", 1, "FINAL select LFNs" );
return result;
}
}