The code that defines the archiveVTI table function is as follows.
package org.apache.derbyTesting.functionTests.tests.lang; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import org.apache.derby.vti.AwareVTI; import org.apache.derby.vti.ForeignTableVTI; import org.apache.derby.vti.ForwardingVTI; import org.apache.derby.vti.RestrictedVTI; import org.apache.derby.vti.Restriction; import org.apache.derby.vti.VTIContext; /** * <p> * This table function acts like a union view on a set of archive tables. * The idea is that the old contents of a main table are periodically * moved to archive tables whose names start with $tableName$suffix. * Each bulk move of rows results in the creation of a new archive table. * The archive tables live in the same schema as the main table and have * its shape. This table function unions the main table together with all * of its archived snapshots. So, for instance, you might have the * following set of tables, which this table function unions together: * </p> * * <pre> * T1 * T1_ARCHIVE_1 * T1_ARCHIVE_2 * ... * T1_ARCHIVE_N * </pre> * * <p> * This table function may appear in user documentation. If you change * the behavior of this table function, make sure that you adjust the * user documentation linked from DERBY-6117. * </p> */ public class ArchiveVTI extends ForwardingVTI implements AwareVTI, RestrictedVTI { ///////////////////////////////////////////////////////////////////// // // CONSTANTS // ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// // // STATE // ///////////////////////////////////////////////////////////////////// private Connection _connection; private String _archiveSuffix; private VTIContext _vtiContext; private ArrayList<String> _tableNames; private int _tableIdx; private String[] _columnNames; private Restriction _restriction; ///////////////////////////////////////////////////////////////////// // // TABLE FUNCTION // ///////////////////////////////////////////////////////////////////// /** * <p> * Entry point for creating an ArchiveVTI which is bound to a Derby * table function by a CREATE FUNCTION statement which looks like * this: * </p> * * <pre> * create function t1( archiveSuffix varchar( 32672 ) ) returns table * ( * keyCol int, * aCol int, * bCol int * ) * language java parameter style derby_jdbc_result_set reads sql data * external name * 'org.apache.derbyTesting.functionTests.tests.lang.ArchiveVTI.archiveVTI' * </pre> * * @param archiveSuffix All of the archive tables have names of the * form $tablename$archiveSuffix. */ public static ArchiveVTI archiveVTI( String archiveSuffix ) throws SQLException { return new ArchiveVTI( archiveSuffix ); } ///////////////////////////////////////////////////////////////////// // // CONSTRUCTOR // ///////////////////////////////////////////////////////////////////// /** Construct from the suffix which flags all of the relevant * tables. */ public ArchiveVTI( String archiveSuffix ) throws SQLException { _connection = DriverManager.getConnection( "jdbc:default:connection" ); _archiveSuffix = archiveSuffix; } ///////////////////////////////////////////////////////////////////// // // AwareVTI BEHAVIOR // ///////////////////////////////////////////////////////////////////// public VTIContext getContext() { return _vtiContext; } public void setContext( VTIContext context ) { _vtiContext = context; } ///////////////////////////////////////////////////////////////////// // // RestrictedVTI BEHAVIOR // ///////////////////////////////////////////////////////////////////// public void initScan ( String[] columnNames, Restriction restriction ) throws SQLException { _columnNames = new String[ columnNames.length ]; System.arraycopy( columnNames, 0, _columnNames, 0, columnNames.length ); _restriction = restriction; } ///////////////////////////////////////////////////////////////////// // // ResultSet BEHAVIOR // ///////////////////////////////////////////////////////////////////// public boolean next() throws SQLException { if ( _tableNames == null ) { getTableNames(); _tableIdx = 0; loadResultSet(); } while ( !super.next() ) { _tableIdx++; if ( _tableIdx >= _tableNames.size() ) { return false; } loadResultSet(); } return true; } public void close() throws SQLException { if ( getWrappedResultSet() != null ) { getWrappedResultSet().close(); } wrapResultSet( null ); _connection = null; } ///////////////////////////////////////////////////////////////////// // // UTILITY METHODS // ///////////////////////////////////////////////////////////////////// /** * <p> * Get cursors on all the tables which we are going to union * together. * </p> */ private void getTableNames() throws SQLException { _tableNames = new ArrayList<String>(); _tableNames.add( getContext().vtiTable() ); DatabaseMetaData dbmd = getConnection().getMetaData(); ResultSet candidates = dbmd.getTables ( null, getContext().vtiSchema(), getContext().vtiTable() + _archiveSuffix + "%", null ); while ( candidates.next() ) { _tableNames.add( candidates.getString( "TABLE_NAME" ) ); } candidates.close(); } /** * <p> * Compile the query against the next table and use its ResultSet * until it's drained. * </p> */ private void loadResultSet() throws SQLException { if ( getWrappedResultSet() != null ) { getWrappedResultSet().close(); } ForeignTableVTI nextRS = new ForeignTableVTI ( getContext().vtiSchema(), _tableNames.get( _tableIdx ), getConnection() ); nextRS.initScan( _columnNames, _restriction ); wrapResultSet( nextRS ); } /** * <p> * Get this database session's connection to the database. * </p> */ private Connection getConnection() throws SQLException { return _connection; } }