package org.reldb.rel.v0.storage;
import java.io.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.file.Files;
import org.reldb.rel.exceptions.*;
import org.reldb.rel.v0.external.DirClassLoader;
import org.reldb.rel.v0.generator.*;
import org.reldb.rel.v0.interpreter.Interpreter;
import org.reldb.rel.v0.languages.tutoriald.parser.*;
import org.reldb.rel.v0.storage.catalog.*;
import org.reldb.rel.v0.storage.relvars.RelvarDefinition;
import org.reldb.rel.v0.storage.relvars.RelvarGlobal;
import org.reldb.rel.v0.storage.relvars.RelvarHeading;
import org.reldb.rel.v0.storage.relvars.RelvarMetadata;
import org.reldb.rel.v0.storage.relvars.RelvarPrivate;
import org.reldb.rel.v0.storage.relvars.RelvarPrivateCell;
import org.reldb.rel.v0.storage.relvars.RelvarReal;
import org.reldb.rel.v0.storage.relvars.RelvarRealMetadata;
import org.reldb.rel.v0.storage.tables.StorageNames;
import org.reldb.rel.v0.storage.tables.Storage;
import org.reldb.rel.v0.storage.tables.RegisteredTupleIterator;
import org.reldb.rel.v0.storage.tables.Table;
import org.reldb.rel.v0.storage.tables.TablePrivate;
import org.reldb.rel.v0.types.Heading;
import org.reldb.rel.v0.types.OrderMap;
import org.reldb.rel.v0.types.Type;
import org.reldb.rel.v0.types.TypeAlpha;
import org.reldb.rel.v0.types.TypeTuple;
import org.reldb.rel.v0.types.builtin.*;
import org.reldb.rel.v0.values.*;
import org.reldb.rel.v0.version.Version;
import org.reldb.rel.v0.vm.Context;
import org.reldb.rel.v0.vm.Instruction;
import org.reldb.rel.v0.vm.Operator;
import org.reldb.rel.v0.vm.VirtualMachine;
import org.reldb.rel.v0.vm.instructions.core.OpInvoke;
import org.reldb.rel.v0.vm.instructions.core.OpInvokeDynamicEvaluate;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.serial.SerialBinding;
import com.sleepycat.bind.serial.StoredClassCatalog;
import com.sleepycat.bind.tuple.StringBinding;
import com.sleepycat.je.*;
public class RelDatabase {
public final static String systemOwner = "Rel";
private Environment environment;
private DatabaseConfig dbConfigurationNormal;
private DatabaseConfig dbConfigurationAllowCreate;
private DatabaseConfig dbConfigurationTemporary;
private DatabaseConfig dbConfigurationTemporaryWithDuplicatesNoComparator;
private DatabaseConfig dbConfigurationMetadataAllowCreateNoComparator;
// Metadata
private ClassCatalog classCatalog;
private StringBinding stringDataBinding;
private DatabaseEntry keyTableEntry = new DatabaseEntry();
private Database relvarDb;
// open real relvar tables
private Hashtable<String, Database> openStorage = new Hashtable<String, Database>();
// temporary private relvar table names
private HashSet<String> tempStorageNames = new HashSet<String>();
// active transaction per thread
private Hashtable<Long, RelTransaction> transactions = new Hashtable<Long, RelTransaction>();
// database constraints
private Hashtable<String, Operator> constraints = new Hashtable<String, Operator>();
// built-in operators
private HashMap<String, OperatorDefinition> builtinops = new HashMap<String, OperatorDefinition>();
// Permanent cached operator definitions.
private HashMap<OperatorSignature, OperatorDefinition> operatorCachePermanent = new HashMap<OperatorSignature, OperatorDefinition>();
// Cached operator definitions
private HashMap<OperatorSignature, OperatorDefinition> operatorCache = new HashMap<OperatorSignature, OperatorDefinition>();
// Cached type definitions
private HashMap<String, org.reldb.rel.v0.types.Type> typeCache = new HashMap<String, org.reldb.rel.v0.types.Type>();
// Active registered tuple iterators
private HashSet<RegisteredTupleIterator> registeredTupleIterators = new HashSet<RegisteredTupleIterator>();
// class loader for external Java-based operators and types
private DirClassLoader dirClassLoader;
// Relative database directory name
private static final String databaseHomeRelative = "Reldb";
// Relative user code directory name
private static final String userCodeHomeRelative = "RelUserCode";
// Rel user Java code package
private static final String relUserCodePackage = databaseHomeRelative + "." + userCodeHomeRelative;
// User code home dir
private String userCodeHome;
// Database home dir
private String databaseHome = databaseHomeRelative;
// Rel home dir
private String homeDir;
// Special mode for caching permanent operators and types
private boolean specialCacheMode = true;
// Additional JAR files for the Java compiler to include in the classpath when compiling.
private String[] additionalJarsForJavaCompilerClasspath = null;
public void setAdditionalJarsForJavaCompilerClasspath(String[] additionalJarsForClasspath) {
this.additionalJarsForJavaCompilerClasspath = additionalJarsForClasspath;
}
public String[] getAdditionalJarsForJavaCompilerClasspath() {
return additionalJarsForJavaCompilerClasspath;
}
/*
* This class is used to set up custom Comparator used by the Berkeley DB for data (not needed for metadata).
*/
private static class ComparisonHandler implements Serializable, Comparator<byte[]> {
private static final long serialVersionUID = 1L;
private static volatile SerialBinding<ValueTuple> tupleBinding;
public ComparisonHandler(SerialBinding<ValueTuple> tupleBinding) {
ComparisonHandler.tupleBinding = tupleBinding;
}
@Override
public int compare(byte[] o1, byte[] o2) {
ValueTuple v1 = tupleBinding.entryToObject(new DatabaseEntry(o1));
ValueTuple v2 = tupleBinding.entryToObject(new DatabaseEntry(o2));
return v1.compareTo(v2);
}
}
private static void applyComparatorTo(Comparator<byte[]> comparator, DatabaseConfig config) {
config.setBtreeComparator(comparator);
config.setOverrideBtreeComparator(true);
config.setDuplicateComparator(comparator);
config.setOverrideDuplicateComparator(true);
}
private String getBerkeleyJavaDBVersion() {
return JEVersion.CURRENT_VERSION.getVersionString();
}
public static void mkdir(String dir) {
File dirf = new File(dir);
if (!dirf.exists()) {
if (!dirf.mkdirs()) {
String msg = "Unable to create directory: " + dirf;
throw new ExceptionFatal("RS0324: " + msg);
}
}
}
private String getClickerFileName() {
return homeDir + File.separator + "ClickToOpen.rdb";
}
private void writeClicker() {
FileWriter writer = null;
try {
writer = new FileWriter(getClickerFileName(), false);
if (writer != null)
writer.close();
} catch (Exception e) {
System.out.println("WARNING: Unable to create " + getClickerFileName());
}
}
private String getExtensionDirectoryName() {
return homeDir + File.separator + "Extensions";
}
private void ensureExtensionDirectoryExists() {
mkdir(getExtensionDirectoryName());
}
public File getExtensionDirectory() {
return new File(getExtensionDirectoryName());
}
private String getVersionFileName() {
return databaseHome + File.separator + "version";
}
private void writeVersion() throws IOException {
FileWriter writer = null;
try {
writer = new FileWriter(getVersionFileName(), false);
writer.write(Integer.toString(Version.getDatabaseVersion()));
} finally {
if (writer != null)
writer.close();
}
}
private int readVersion() {
List<String> lines = null;
try {
lines = Files.readAllLines(new File(getVersionFileName()).toPath());
} catch (IOException e1) {
return -1;
}
if (lines.isEmpty())
return -1;
try {
return Integer.parseInt(lines.get(0));
} catch (NumberFormatException nfe) {
return -1;
}
}
public void open(File envHome, boolean canCreateDb, PrintStream outputStream) throws DatabaseFormatVersionException {
System.out.println("Opening database in " + envHome + "\nIf it doesn't exist, we'll " + ((canCreateDb) ? "try to create it" : "cause an error") + ".");
String usingBerkeleyJavaDBVersion = getBerkeleyJavaDBVersion();
if (!usingBerkeleyJavaDBVersion.equals(Version.expectedBerkeleyDBVersion))
throw new ExceptionFatal("RS0323: Expected to find Berkeley Java DB version " + Version.expectedBerkeleyDBVersion + " but found version " + usingBerkeleyJavaDBVersion + ".\nAn attempted update or re-installation has probably failed.\nPlease make sure " + Version.getBerkeleyDbJarFilename() + " is not read-only, then try the update or re-installation again.");
homeDir = envHome.getAbsolutePath();
if (homeDir.endsWith("."))
homeDir = homeDir.substring(0, homeDir.length() - 1);
if (!homeDir.endsWith(java.io.File.separator))
homeDir += java.io.File.separator;
databaseHome = homeDir + databaseHomeRelative;
if (!(new File(databaseHome)).exists())
if (!canCreateDb)
throw new ExceptionSemantic("RS0406: Database " + homeDir + " either doesn't exist or isn't a Rel database.");
else {
mkdir(databaseHome);
try {
writeVersion();
} catch (IOException ioe) {
throw new ExceptionSemantic("RS0408: Can't write version file in database in " + homeDir + ".");
}
}
else {
int detectedVersion = readVersion();
if (detectedVersion < 0) {
throw new ExceptionSemantic("RS0407: Database in " + homeDir + " has no version information, or it's invalid. The database must be upgraded manually.\nBack it up with the version of Rel used to create it and load the backup into a new database.");
} else if (detectedVersion < Version.getDatabaseVersion()) {
String msg = "RS0410: Database requires conversion from format v" + detectedVersion + " to format v" + Version.getDatabaseVersion();
throw new DatabaseFormatVersionException(msg, detectedVersion);
} else if (detectedVersion > Version.getDatabaseVersion()) {
throw new ExceptionSemantic("RS0409: Database in " + homeDir + " appears to have been created by a newer version of Rel than this one.\nOpen it with the latest version of Rel.");
}
}
writeClicker();
ensureExtensionDirectoryExists();
userCodeHome = databaseHome + java.io.File.separator + userCodeHomeRelative;
dirClassLoader = new DirClassLoader(homeDir);
try {
EnvironmentConfig environmentConfig = new EnvironmentConfig();
environmentConfig.setReadOnly(false);
environmentConfig.setAllowCreate(true);
environmentConfig.setTransactional(true);
environmentConfig.setTxnSerializableIsolation(true);
dbConfigurationNormal = new DatabaseConfig();
dbConfigurationNormal.setReadOnly(false);
dbConfigurationNormal.setAllowCreate(false);
dbConfigurationNormal.setTransactional(true);
dbConfigurationNormal.setSortedDuplicates(false);
dbConfigurationAllowCreate = new DatabaseConfig();
dbConfigurationAllowCreate.setReadOnly(false);
dbConfigurationAllowCreate.setAllowCreate(true);
dbConfigurationAllowCreate.setTransactional(true);
dbConfigurationAllowCreate.setSortedDuplicates(false);
dbConfigurationTemporary = new DatabaseConfig();
dbConfigurationTemporary.setReadOnly(false);
dbConfigurationTemporary.setAllowCreate(true);
dbConfigurationTemporary.setTransactional(false);
dbConfigurationTemporary.setSortedDuplicates(false);
dbConfigurationTemporary.setTemporary(true);
dbConfigurationTemporaryWithDuplicatesNoComparator = new DatabaseConfig();
dbConfigurationTemporaryWithDuplicatesNoComparator.setReadOnly(false);
dbConfigurationTemporaryWithDuplicatesNoComparator.setAllowCreate(true);
dbConfigurationTemporaryWithDuplicatesNoComparator.setTransactional(false);
dbConfigurationTemporaryWithDuplicatesNoComparator.setSortedDuplicates(true);
dbConfigurationTemporaryWithDuplicatesNoComparator.setTemporary(true);
dbConfigurationMetadataAllowCreateNoComparator = new DatabaseConfig();
dbConfigurationMetadataAllowCreateNoComparator.setReadOnly(false);
dbConfigurationMetadataAllowCreateNoComparator.setAllowCreate(true);
dbConfigurationMetadataAllowCreateNoComparator.setTransactional(true);
dbConfigurationMetadataAllowCreateNoComparator.setSortedDuplicates(false);
// Open the class catalog
classCatalog = new ClassCatalog(databaseHome, environmentConfig, dbConfigurationMetadataAllowCreateNoComparator);
// Set up database BTREE and duplicate comparators.
Comparator<byte[]> comparator = new ComparisonHandler(classCatalog.getTupleBinding());
applyComparatorTo(comparator, dbConfigurationNormal);
applyComparatorTo(comparator, dbConfigurationAllowCreate);
applyComparatorTo(comparator, dbConfigurationTemporary);
// Open the main database environment
environment = new Environment(new File(databaseHome), environmentConfig);
// Get a code generator
Generator generator = new Generator(this, System.out);
// Data for key table entries
stringDataBinding = new StringBinding();
stringDataBinding.objectToEntry("", keyTableEntry);
// Open the metadata db.
relvarDb = environment.openDatabase(null, "_Relvars", dbConfigurationMetadataAllowCreateNoComparator);
// Declare the Catalog
Catalog catalog = new Catalog(this);
// Catalog build phase 0
catalog.generatePhase0(generator);
// Construct builtin-in primitive types and operators
specialCacheMode = true;
BuiltinOperators.buildOperators(this);
BuiltinTypeBuilder.buildTypes(this);
operatorCachePermanent = operatorCache;
specialCacheMode = false;
// Construct built-in type types.
if (!isTypeExists(generator, "TypeInfo")) {
try {
System.out.println("Creating TypeInfo types.");
Interpreter.executeStatementPrivileged(this, "TYPE TypeInfo UNION;", "Rel", System.out);
Interpreter.executeStatementPrivileged(this, "TYPE Scalar IS {TypeInfo POSSREP {TypeName CHAR}};", "Rel", System.out);
Interpreter.executeStatementPrivileged(this, "TYPE NonScalar IS {TypeInfo POSSREP {Kind CHAR, Attributes RELATION {AttrName CHAR, AttrType TypeInfo}}};", "Rel", System.out);
} catch (ParseException e) {
e.printStackTrace();
}
}
// Catalog build phase 1
catalog.generatePhase1(generator);
// Construct DDL log relvar.
if (!isRelvarExists(Catalog.relvarDefinitionHistory))
try {
System.out.println("Creating " + Catalog.relvarDefinitionHistory + " relvar.");
Interpreter.executeStatementPrivileged(this, "VAR " + Catalog.relvarDefinitionHistory + " REAL RELATION {Definition CHAR, SerialNumber INTEGER} KEY {SerialNumber};", "Rel", System.out);
} catch (ParseException e) {
e.printStackTrace();
}
// Prepare for battle.
reset();
loadConstraints(outputStream);
System.out.println("Database " + envHome + " is open.");
} catch (DatabaseException db) {
String msg = "Unable to open database: " + db.getMessage();
outputStream.println(msg);
db.printStackTrace(System.out);
throw new ExceptionFatal("RS0325: " + msg);
}
}
/** Return the directory that contains the database. */
public String getHomeDir() {
return homeDir;
}
public void registerTupleIterator(RegisteredTupleIterator registeredTupleIterator) {
registeredTupleIterators.add(registeredTupleIterator);
}
public void unregisterTupleIterator(RegisteredTupleIterator registeredTupleIterator) {
registeredTupleIterators.remove(registeredTupleIterator);
}
public boolean isOpen() {
return environment != null;
}
// Close the environment
public void close() {
if (environment == null) {
System.out.println("WARNING: Attempting to re-close a closed database!");
System.out.println("It's not a problem, but we'll tell you where it's coming from so maybe you can fix it:");
(new Throwable()).printStackTrace();
return;
}
System.out.println("Closing database in " + homeDir);
System.out.println("\tClosing active tuple iterators in " + homeDir);
int activeTupleIterators = 0;
try {
for (RegisteredTupleIterator tupleIterator: registeredTupleIterators)
if (tupleIterator.forceClose())
activeTupleIterators++;
} catch (Exception e) {
System.err.println("\tError closing active tuple iterators: " + homeDir + ": " + e.toString());
}
if (activeTupleIterators == 1)
System.err.println("\t" + activeTupleIterators + " active tuple iterator was closed.");
else if (activeTupleIterators > 1)
System.err.println("\t" + activeTupleIterators + " active tuple iterators were closed.");
System.out.println("\tCommitting open transactions in " + homeDir);
int openTransactions = 0;
for (RelTransaction transaction: transactions.values())
while (transaction.getReferenceCount() > 0)
try {
commitTransaction(transaction);
openTransactions++;
} catch(DatabaseException dbe) {
System.err.println("\tError committing active transactions " + homeDir + ": " + dbe.toString());
} catch (Exception e) {
System.err.println("\tUnknown shutdown error 1: " + e);
}
if (openTransactions == 1)
System.err.println("\t" + openTransactions + " open transaction was closed.");
else if (openTransactions > 1)
System.err.println("\t" + openTransactions + " open transactions were closed.");
System.out.println("\tClosing relvars in " + homeDir);
try {
for (Database table: openStorage.values())
table.close();
} catch(DatabaseException dbe) {
System.err.println("\tError closing internal tables " + homeDir + ": " + dbe.toString());
} catch (Exception e) {
System.err.println("\tUnknown shutdown error 2: " + e);
}
System.out.println("\tPurging temporary data in " + homeDir);
try {
for (String tableName: tempStorageNames)
environment.removeDatabase(null, tableName);
} catch(DatabaseException dbe) {
System.err.println("\tError removing temporary data storage " + homeDir + ": " + dbe.toString());
} catch (Exception e) {
System.err.println("\tUnknown shutdown error 3: " + e);
}
System.out.println("\tTemporary data purged in " + homeDir);
try {
relvarDb.close();
} catch(DatabaseException dbe) {
System.err.println("\tError closing the relvarDb " + homeDir + ": " + dbe.toString());
} catch (Exception e) {
System.err.println("\tUnknown shutdown error 4: " + e);
}
System.out.println("\tClosing environment in " + homeDir);
try {
environment.close();
} catch(DatabaseException dbe) {
System.err.println("\tError closing the environment " + homeDir + ": " + dbe.toString());
} catch (Exception e) {
System.err.println("\tUnknown shutdown error 5: " + e);
}
environment = null;
try {
classCatalog.close();
} catch(DatabaseException dbe) {
System.err.println("\tError closing the ClassCatalog in " + homeDir + ": " + dbe.toString());
} catch (Exception e) {
System.err.println("\tUnknown shutdown error 6: " + e);
}
System.out.println("Database " + homeDir + " is closed.");
}
public void reset() {
operatorCache = new HashMap<OperatorSignature, OperatorDefinition>();
typeCache.clear();
}
/** Get the user Java code definition directory. */
public String getJavaUserSourcePath() {
return userCodeHome;
}
public static String getRelUserCodePackage() {
return relUserCodePackage;
}
private Database openDatabaseRaw(Transaction txn, String tabName, DatabaseConfig configuration) throws DatabaseException {
Database db = environment.openDatabase(txn, tabName, configuration);
openStorage.put(tabName, db);
return db;
}
// This gnarly bit of code ensures a Berkeley Database is open and valid. If it was opened in a transaction
// that was rolled back, it will throw an exception when an attempt is made to open a cursor. In that
// case, it needs to be re-opened in the current transaction.
private Database openDatabase(Transaction txn, String tabName, DatabaseConfig configuration) {
Database db = openStorage.get(tabName);
try {
if (db == null)
db = openDatabaseRaw(txn, tabName, configuration);
else
db.openCursor(txn, null).close();
} catch (IllegalStateException de) {
try {
db.close();
db = openDatabaseRaw(txn, tabName, configuration);
} catch (IllegalStateException de2) {
de2.printStackTrace();
throw new ExceptionFatal("RS0326: openDatabase: re-open failed: " + de2);
}
}
return db;
}
private void closeDatabase(String tabName) throws DatabaseException {
Database table = openStorage.get(tabName);
if (table != null) {
table.close();
openStorage.remove(tabName);
}
}
public ValueOperator compileAnonymousOperator(String source, PrintStream outputStream) {
Interpreter interpreter = new Interpreter(RelDatabase.this, outputStream);
try {
return interpreter.compileAnonymousOperator(source);
} catch (ParseException pe) {
throw new ExceptionSemantic("RS0288: Failed compiling anonymous operator " + source + ": " + pe.toString());
}
}
private void loadConstraints(final PrintStream outputStream) {
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
Interpreter interpreter = new Interpreter(RelDatabase.this, outputStream);
RelvarSystem constraintsRelvar = (RelvarSystem)openGlobalRelvar(Catalog.relvarConstraints);
TupleIterator ti = constraintsRelvar.iterator(interpreter.getGenerator());
try {
while (ti.hasNext()) {
ValueTuple constraintEntry = ti.next();
String constraintName = constraintEntry.getValues()[0].toString();
String source = constraintEntry.getValues()[1].toString();
try {
Operator constraintCheckOperator = interpreter.getGenerator().beginConstraintDefinition();
interpreter.compileStatement("RETURN " + source + ";");
interpreter.getGenerator().endConstraintDefinition();
constraints.put(constraintName, constraintCheckOperator);
} catch (ParseException pe) {
pe.printStackTrace();
throw new ExceptionFatal("RS0327: Failed loading constraint " + constraintName + ": " + pe.toString());
}
}
} finally {
ti.close();
}
return null;
}
}).execute(this);
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0328: loadConstraints failed: " + t);
}
}
private boolean checkConstraint(VirtualMachine vm, Operator constraintCheck) {
vm.execute(constraintCheck);
return ((ValueBoolean)vm.pop()).booleanValue();
}
/** Check the known constraints for validity. If a constraint fails, return its name.
If all constraints succeed, return null. */
public synchronized String checkConstraints(final PrintStream outputStream) {
try {
return (String)((new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
VirtualMachine vm = new VirtualMachine(new Generator(RelDatabase.this, System.out), RelDatabase.this, outputStream);
for (Map.Entry<String, Operator> entry: constraints.entrySet()) {
String constraintName = entry.getKey();
try {
if (!checkConstraint(vm, entry.getValue()))
return constraintName;
} catch (ExceptionSemantic es) {
throw new ExceptionSemantic("RS0393: error in CONSTRAINT " + constraintName + ": " + es.getMessage(), es);
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0329: checkConstraints failed: " + t);
}
}
return null;
}
}).execute(this));
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable e) {
e.printStackTrace();
throw new ExceptionFatal("RS0392: checkConstraints failed: " + e);
}
}
public String getNativeDBVersion() {
return "Oracle Berkeley DB Java Edition version " + getBerkeleyJavaDBVersion();
}
// Store a Relvar name and associated metadata.
public void putRelvarMetadata(Transaction txn, String relvarName, RelvarMetadata metadata) throws DatabaseException {
if (metadata.getCreationSequence() == -1)
metadata.setCreationSequence(getUniqueID());
DatabaseEntry theKey = new DatabaseEntry();
stringDataBinding.objectToEntry(relvarName, theKey);
DatabaseEntry theData = new DatabaseEntry();
classCatalog.getRelvarMetadataBinding().objectToEntry(metadata, theData);
relvarDb.put(txn, theKey, theData);
}
// Retrieve a Relvar's metadata. Return null if not found.
public synchronized RelvarMetadata getRelvarMetadata(Transaction txn, String relvarName) throws DatabaseException {
DatabaseEntry theKey = new DatabaseEntry();
DatabaseEntry foundData = new DatabaseEntry();
stringDataBinding.objectToEntry(relvarName, theKey);
if (relvarDb.get(txn, theKey, foundData, LockMode.DEFAULT) == OperationStatus.SUCCESS)
return classCatalog.getRelvarMetadataBinding().entryToObject(foundData);
return null;
}
// Remove a Relvar's metadata.
private void dropRelvarMetadata(Transaction txn, String relvarName) throws DatabaseException {
DatabaseEntry theKey = new DatabaseEntry();
stringDataBinding.objectToEntry(relvarName, theKey);
if (relvarDb.delete(txn, theKey) != OperationStatus.SUCCESS)
throw new ExceptionFatal("RS0330: unable to drop relvar metadata for " + relvarName);
}
private final File getUniqueIDFile() {
return new File(databaseHome + java.io.File.separatorChar + "unique.id");
}
// Set the next unique ID. No-op if the value is less than what the next ID would have been.
public synchronized void setUniqueID(long newid) {
File uniquidFile = getUniqueIDFile();
long id = 0, nextid = 1;
try {
DataInputStream dis = new DataInputStream(new FileInputStream(uniquidFile));
id = dis.readLong();
nextid = id + 1;
dis.close();
} catch (Throwable t) {
System.out.println("Creating new ID file.");
}
try {
DataOutputStream dos = new DataOutputStream(new FileOutputStream(uniquidFile));
if (newid < nextid)
dos.writeLong(nextid);
else
dos.writeLong(newid);
dos.close();
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0331: " + t.toString());
}
}
// Obtain a unique ID
public synchronized long getUniqueID() {
File uniquidFile = getUniqueIDFile();
long id = 0, nextid = 1;
try {
DataInputStream dis = new DataInputStream(new FileInputStream(uniquidFile));
id = dis.readLong();
nextid = id + 1;
dis.close();
} catch (Throwable t) {
System.out.println("Creating new ID file.");
}
try {
DataOutputStream dos = new DataOutputStream(new FileOutputStream(uniquidFile));
dos.writeLong(nextid);
dos.close();
return id;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0331: " + t.toString());
}
}
// Obtain the current thread ID
private Long getThreadID() {
return new Long(Thread.currentThread().getId());
}
/** Return true if a relvar exists. */
private boolean isRelvarExists(Transaction txn, String name) throws DatabaseException {
return (getRelvarMetadata(txn, name) != null);
}
public EntryBinding<ValueTuple> getTupleBinding() {
return classCatalog.getTupleBinding();
}
public DatabaseEntry getKeyTableEntry() {
return keyTableEntry;
}
StoredClassCatalog getClassCatalog() {
return classCatalog.getStoredClassCatalog();
}
/** Begin transaction. */
public synchronized RelTransaction beginTransaction() {
Long threadID = getThreadID();
RelTransaction currentTransaction = transactions.get(threadID);
if (currentTransaction == null) {
try {
Transaction txn = environment.beginTransaction(null, null);
// TODO - parameterise setLockTimeout value somewhere
txn.setLockTimeout(10, TimeUnit.SECONDS);
currentTransaction = new RelTransaction(txn);
transactions.put(threadID, currentTransaction);
} catch (DatabaseException dbe) {
dbe.printStackTrace();
throw new ExceptionFatal("RS0332: unable to begin new transaction: " + dbe);
}
} else
currentTransaction.addReference();
return currentTransaction;
}
// Get current transaction in this thread. Return null if there isn't one.
private RelTransaction getCurrentTransaction() {
Long threadID = getThreadID();
return transactions.get(threadID);
}
/** Commit specified transaction */
private void commitTransactionUnsynchronized(RelTransaction txn) {
txn.commit();
if (txn.getReferenceCount() == 0)
transactions.remove(getThreadID());
}
/** Commit specified transaction. */
public synchronized void commitTransaction(RelTransaction txn) {
commitTransactionUnsynchronized(txn);
}
/** Commit current transaction. */
public synchronized void commitTransaction() {
RelTransaction currentTransaction = getCurrentTransaction();
if (currentTransaction == null)
throw new ExceptionSemantic("RS0208: No transaction is active.");
commitTransactionUnsynchronized(currentTransaction);
}
/** Roll back specified transaction. */
private void rollbackTransactionUnsynchronized(RelTransaction txn) {
txn.abort();
if (txn.getReferenceCount() == 0)
transactions.remove(getThreadID());
}
/** Roll back specified transaction. */
synchronized void rollbackTransaction(RelTransaction txn) {
rollbackTransactionUnsynchronized(txn);
}
/** Roll back current transaction if there is one. Silently return if there isn't one. */
public synchronized void rollbackTransactionIfThereIsOne() {
reset();
RelTransaction currentTransaction = getCurrentTransaction();
if (currentTransaction == null)
return;
while (currentTransaction.getReferenceCount() > 0)
rollbackTransactionUnsynchronized(currentTransaction);
}
/** Roll back current transaction. Throw an exception if there isn't one. */
public synchronized void rollbackTransaction() {
RelTransaction currentTransaction = getCurrentTransaction();
if (currentTransaction == null)
throw new ExceptionSemantic("RS0209: No transaction is active.");
rollbackTransactionUnsynchronized(currentTransaction);
}
/** Obtain Catalog as a TupleIterator */
public synchronized TupleIterator getCatalogTupleIterator(final Generator generator) {
return new RegisteredTupleIterator(generator.getDatabase()) {
DatabaseEntry foundKey = new DatabaseEntry();
DatabaseEntry foundData = new DatabaseEntry();
RelvarMetadata current = null;
String currentKey = null;
public boolean hasNext() {
if (current != null)
return true;
try {
if (cursor == null) {
txn = beginTransaction();
cursor = relvarDb.openCursor(txn.getTransaction(), null);
}
if (cursor.getNext(foundKey, foundData, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
current = classCatalog.getRelvarMetadataBinding().entryToObject(foundData);
currentKey = stringDataBinding.entryToObject(foundKey);
return true;
}
} catch (DatabaseException exp) {
exp.printStackTrace();
throw new ExceptionFatal("RS0333: Unable to get next Catalog entry: " + exp);
}
return false;
}
public ValueTuple next() {
if (hasNext())
try {
RelvarHeading relvarHeading = current.getHeadingDefinition(RelDatabase.this);
Heading heading = relvarHeading.getHeading();
Value typeInfo = generator.getTypeOf(heading, "RELATION");
int keyCount = relvarHeading.getKeyCount();
ValueRelation keysRelation = new ValueRelation(generator) {
private static final long serialVersionUID = 1L;
@Override
public TupleIterator newIterator() {
return new TupleIterator() {
int keyNumber = 0;
@Override
public boolean hasNext() {
return keyNumber < keyCount;
}
@Override
public ValueTuple next() {
ValueRelationLiteral keyDef = new ValueRelationLiteral(generator);
for (String attributeName: relvarHeading.getKey(keyNumber).getNames()) {
ValueCharacter attributeNameChar = ValueCharacter.select(generator, attributeName);
keyDef.insert(new ValueTuple(generator, new Value[] {attributeNameChar}));
}
keyNumber++;
return new ValueTuple(generator, new Value[] {keyDef});
}
@Override
public void close() {
}
};
}
@Override
public int hashCode() {
return 0;
}
};
// This must parallel the Heading defined by getNewHeading() in RelvarCatalogMetadata
Value[] values = new Value[] {
ValueCharacter.select(generator, currentKey),
ValueCharacter.select(generator, current.getSourceDefinition()),
ValueCharacter.select(generator, current.getOwner()),
ValueInteger.select(generator, current.getCreationSequence()),
ValueBoolean.select(generator, current.isVirtual()),
ValueBoolean.select(generator, current.isExternal()),
typeInfo,
keysRelation
};
return new ValueTuple(generator, values);
} finally {
current = null;
}
throw new NoSuchElementException();
}
};
}
/** Record dependencies */
private void addDependencies(Generator generator, String object, String dependencyRelvarName, Collection<String> uses) {
RelvarSystemDependencies dependencyRelvar = (RelvarSystemDependencies)openGlobalRelvar(dependencyRelvarName);
for (String usesname: uses)
dependencyRelvar.insertInternal(generator, object, usesname);
}
/** Remove object dependencies */
private void removeDependencies(Generator generator, final String object, String dependencyRelvarName) {
RelvarSystemDependencies dependencyRelvar = (RelvarSystemDependencies)openGlobalRelvar(dependencyRelvarName);
dependencyRelvar.delete(generator, new TupleFilter() {
public boolean filter(ValueTuple tuple) {
// index 0 is 'Object' attribute
return (((ValueCharacter)tuple.getValues()[0]).stringValue().equals(object));
}
});
}
/** Append dependency descriptions to output StringBuffer. */
private void obtainDependencies(Generator generator, final StringBuffer output, final String object, String dependencyRelvarName, final String usedBy) {
RelvarSystemDependencies dependencyRelvar = (RelvarSystemDependencies)openGlobalRelvar(dependencyRelvarName);
TupleIterator dependencies = dependencyRelvar.getValue(generator).select(new TupleFilter() {
public boolean filter(ValueTuple tuple) {
// index 1 is 'Uses' attribute
return (((ValueCharacter)tuple.getValues()[1]).stringValue().equals(object));
}
}).iterator();
try {
int referenceCount = 0;
while (dependencies.hasNext()) {
if (referenceCount++ < 5) {
// index 0 is 'Object' attribute
output.append("\n\tused by " + usedBy + " " + dependencies.next().getValues()[0]);
} else {
output.append(" and more...");
break;
}
}
} finally {
dependencies.close();
}
}
/** Return true if a type of the given name has been loaded. Return null if it can't be found. */
public synchronized org.reldb.rel.v0.types.Type loadType(Generator generator, String typeName) {
if (typeName.equalsIgnoreCase("CHARACTER") || typeName.equalsIgnoreCase("CHAR"))
return TypeCharacter.getInstance();
else if (typeName.equalsIgnoreCase("INTEGER") || typeName.equalsIgnoreCase("INT"))
return TypeInteger.getInstance();
else if (typeName.equalsIgnoreCase("BOOLEAN") || typeName.equalsIgnoreCase("BOOL"))
return TypeBoolean.getInstance();
else if (typeName.equalsIgnoreCase("RATIONAL") || typeName.equalsIgnoreCase("RAT"))
return TypeRational.getInstance();
org.reldb.rel.v0.types.Type type = typeCache.get(typeName);
if (type == null) {
try {
RelvarTypes typesRelvar = (RelvarTypes)openGlobalRelvar(Catalog.relvarTypes);
ValueTuple typeTuple = typesRelvar.getTupleForKey(generator, typeName);
if (typeTuple == null)
return null;
Interpreter interpreter = new Interpreter(RelDatabase.this, System.out);
// Make sure immediate supertypes are loaded first
ValueRelation superTypeNames = (ValueRelation)typeTuple.getValues()[6];
if (superTypeNames.getCardinality() > 0) {
for (TupleIterator it = superTypeNames.iterator(); it.hasNext();)
loadType(generator, it.next().getValues()[0].stringValue());
// this type should now be cached, because all subtypes of a loaded type will be loaded,
// so if we load the immediate supertype of this type, this type should already be loaded
type = typeCache.get(typeName);
if (type != null)
return type;
}
String language = typeTuple.getValues()[4].toString();
if (language.equals("Rel")) {
String source = typeTuple.getValues()[1].toString();
try {
interpreter.getGenerator().beginTypeRetrieval();
interpreter.compileStatement(source);
type = interpreter.getGenerator().endTypeRetrieval();
} catch (ParseException pe) {
pe.printStackTrace();
throw new ExceptionFatal("RS0335: Failed loading type " + typeName + ": " + pe.toString());
}
} else if (language.equals("Java")) {
try {
Class<?>typeClass = (Class<?>) loadClass(typeName);
if (typeClass == null)
throw new ExceptionSemantic("RS0210: Unable to load class " + typeName);
Constructor<?> ctor = typeClass.getConstructor(new Class[] {org.reldb.rel.v0.generator.Generator.class});
if (ctor == null)
throw new ExceptionSemantic("RS0211: Unable to find a constructor of the form " + typeName + "(Generator generator)");
type = (org.reldb.rel.v0.types.Type)(ctor.newInstance(generator));
} catch (InstantiationException ie) {
ie.printStackTrace();
throw new ExceptionFatal("RS0336: Unable to load TYPE " + typeName + " [1]: " + ie.getMessage());
} catch (IllegalAccessException iae) {
iae.printStackTrace();
throw new ExceptionFatal("RS0337: Unable to load TYPE " + typeName + " [2]: " + iae.getMessage());
}
} else
throw new ExceptionFatal("RS0338: Unrecognised language '" + language + "' in TYPE " + typeName);
typeCache.put(typeName, type);
// load subtypes
ValueRelation subTypeNames = (ValueRelation)typeTuple.getValues()[5];
for (TupleIterator it = subTypeNames.iterator(); it.hasNext();)
loadType(generator, it.next().getValues()[0].stringValue());
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0339: loadType failed: " + t);
}
}
return type;
}
/** Return true if type exists */
public synchronized boolean isTypeExists(Generator generator, String typeName) {
if (typeCache.containsKey(typeName))
return true;
RelvarTypes typesRelvar = (RelvarTypes)openGlobalRelvar(Catalog.relvarTypes);
return (typesRelvar.getTupleForKey(generator, typeName) != null);
}
private void recordDDL(Generator generator, Transaction txn, String newDefinition) {
try {
if (newDefinition.trim().length() == 0)
return;
RelvarMetadata metadata = getRelvarMetadata(txn, Catalog.relvarDefinitionHistory);
if (metadata == null)
return;
RelvarGlobal relvarDefinitionHistory = metadata.getRelvar(Catalog.relvarDefinitionHistory, RelDatabase.this);
ValueRelation currentValue = relvarDefinitionHistory.getValue(generator);
long nextID;
if (currentValue.getCardinality() > 0) {
SelectOrder orderSelection = new SelectOrder();
OrderMap map = new OrderMap(relvarDefinitionHistory.getHeadingDefinition().getHeading(), orderSelection );
nextID = currentValue.sort(map).max(1).longValue() + 1; // index 1 is SerialNumber attribute
} else
nextID = 1;
Value[] rawTuple = new Value[] {
ValueCharacter.select(generator, newDefinition), // Definition attribute
ValueInteger.select(generator, nextID) // SerialNumber attribute
};
relvarDefinitionHistory.insert(generator, new ValueTuple(generator, rawTuple));
} catch (Exception e) {
System.out.println("DefinitionHistory not logged due to " + e + ": " + newDefinition);
}
}
/** Create type */
public synchronized void createType(final Generator generator, final String typeName, final String src, final String owner, final String language, final References references, final String superTypeName) {
if (isTypeExists(generator, typeName))
throw new ExceptionSemantic("RS0212: TYPE " + typeName + " is already in the database.");
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
RelvarTypes typesRelvar = (RelvarTypes)openGlobalRelvar(Catalog.relvarTypes);
Vector<String> superTypeNames = new Vector<String>();
if (superTypeName != null)
superTypeNames.add(superTypeName);
typesRelvar.insertInternal(generator, typeName, src, owner, language, superTypeNames);
// Remove any self-reference
references.removeReferenceToType(typeName);
// Add dependencies
addDependencies(generator, typeName, Catalog.relvarDependenciesTypeOperator, references.getReferencedOperators());
addDependencies(generator, typeName, Catalog.relvarDependenciesTypeRelvar, references.getReferencedRelvars());
addDependencies(generator, typeName, Catalog.relvarDependenciesTypeType, references.getReferencedTypes());
// if this is a subtype, update the immediate supertype
if (superTypeName != null) {
// TODO - maybe force reload of supertype here?
typesRelvar.addSubtype(generator, superTypeName, typeName);
}
recordDDL(generator, txn, src);
return null;
}
}).execute(this);
if (!specialCacheMode)
reset();
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0340: createType failed: " + t);
}
}
private void removeGeneratedOperators(Generator generator, final String typeName) {
ArrayList<String> dropOps = new ArrayList<String>();
RelvarOperators operatorsRelvar = (RelvarOperators)openGlobalRelvar(Catalog.relvarOperators);
TupleIterator operatorsIterator = operatorsRelvar.getValue(generator).iterator();
try {
while (operatorsIterator.hasNext()) {
ValueTuple operatorTuple = operatorsIterator.next();
ValueRelation implementations = (ValueRelation)operatorTuple.getValues()[1]; // attribute 1 of operator tuple is implementations
TupleIterator implementationsIterator = implementations.iterator();
try {
while (implementationsIterator.hasNext()) {
ValueTuple implementationTuple = implementationsIterator.next();
if (((ValueCharacter)implementationTuple.getValues()[4]).stringValue().equals(typeName)) { // attribute 4 of implementation tuple is generation type
String opSignatureText = ((ValueCharacter)implementationTuple.getValues()[0]).stringValue(); // attr 0 of implementation tuple is signature
dropOps.add(opSignatureText);
}
}
} finally {
implementationsIterator.close();
}
}
} finally {
operatorsIterator.close();
}
for (String opSignatureText: dropOps) {
Interpreter interpreter = new Interpreter(RelDatabase.this, System.out);
OperatorSignature signature;
try {
signature = interpreter.getOperatorSignature(opSignatureText);
} catch (ParseException pe) {
pe.printStackTrace();
throw new ExceptionFatal("RS0341: Unable to retrieve operator signature for '" + opSignatureText + "': " + pe.toString());
}
dropOperatorInternal(generator, signature);
}
}
/** Drop type */
public synchronized void dropType(final Generator generator, final String typeName) {
final org.reldb.rel.v0.types.Type type = loadType(generator, typeName);
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
Generator generator = new Generator(RelDatabase.this, System.out);
removeGeneratedOperators(generator, typeName);
StringBuffer dependencies = new StringBuffer();
obtainDependencies(generator, dependencies, typeName, Catalog.relvarDependenciesOperatorType, "OPERATOR");
obtainDependencies(generator, dependencies, typeName, Catalog.relvarDependenciesRelvarType, "VAR");
obtainDependencies(generator, dependencies, typeName, Catalog.relvarDependenciesTypeType, "TYPE");
if (dependencies.length() > 0)
throw new ExceptionSemantic("RS0213: Type " + typeName + " may not be dropped due to dependencies:" + dependencies);
RelvarTypes typesRelvar = (RelvarTypes)openGlobalRelvar(Catalog.relvarTypes);
typesRelvar.deleteInternal(generator, typeName);
removeDependencies(generator, typeName, Catalog.relvarDependenciesTypeOperator);
removeDependencies(generator, typeName, Catalog.relvarDependenciesTypeRelvar);
removeDependencies(generator, typeName, Catalog.relvarDependenciesTypeType);
dirClassLoader.unload(getRelUserCodePackage() + "." + typeName);
// if this is a subtype, update the immediate supertype
if (type instanceof TypeAlpha) {
TypeAlpha udt = (TypeAlpha)type;
if (udt.isSubtype()) {
// TODO - maybe force reload of supertype here?
String superTypeName = udt.getSupertype().getSignature();
typesRelvar.removeSubtype(generator, superTypeName, typeName);
}
}
typeCache.remove(typeName);
recordDDL(generator, txn, "DROP TYPE typeName;");
return null;
}
}).execute(this);
reset();
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0342: dropType failed: " + t);
}
}
/** Return true if constraint exists */
public synchronized boolean isConstraintExists(String constraintName) {
return constraints.containsKey(constraintName);
}
/** Create constraint. */
public synchronized void createConstraint(final Generator generator, final VirtualMachine vm, final String constraintName, final String sourceCode, final Operator operator, final String owner, final References references) {
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
if (isConstraintExists(constraintName))
throw new ExceptionSemantic("RS0214: Constraint " + constraintName + " already exists.");
if (!checkConstraint(vm, operator))
throw new ExceptionSemantic("RS0215: Constraint " + constraintName + " returns false.");
RelvarSystem constraintsRelvar = (RelvarSystem)openGlobalRelvar(Catalog.relvarConstraints);
constraintsRelvar.insertInternal(generator, constraintName, sourceCode, owner);
addDependencies(generator, constraintName, Catalog.relvarDependenciesConstraintOperator, references.getReferencedOperators());
addDependencies(generator, constraintName, Catalog.relvarDependenciesConstraintRelvar, references.getReferencedRelvars());
addDependencies(generator, constraintName, Catalog.relvarDependenciesConstraintType, references.getReferencedTypes());
constraints.put(constraintName, operator);
recordDDL(generator, txn, "CONSTRAINT " + constraintName + " " + sourceCode + ";");
return null;
}
}).execute(this);
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0343: createConstraint failed: " + t);
}
}
/** Drop constraint. */
public synchronized void dropConstraint(final Generator generator, final String constraintName) {
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
RelvarSystem constraintsRelvar = (RelvarSystem)openGlobalRelvar(Catalog.relvarConstraints);
constraintsRelvar.deleteInternal(generator, constraintName);
removeDependencies(generator, constraintName, Catalog.relvarDependenciesConstraintOperator);
removeDependencies(generator, constraintName, Catalog.relvarDependenciesConstraintRelvar);
removeDependencies(generator, constraintName, Catalog.relvarDependenciesConstraintType);
constraints.remove(constraintName);
recordDDL(generator, txn, "DROP CONSTRAINT " + constraintName + ";");
return null;
}
}).execute(this);
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0344: dropConstraint failed: " + t);
}
}
/** Return true if operator exists */
public synchronized boolean isOperatorExists(OperatorSignature signature) {
RelvarOperators operatorsRelvar = (RelvarOperators)openGlobalRelvar(Catalog.relvarOperators);
return (operatorsRelvar.isOperatorExists(new Generator(RelDatabase.this, System.out), signature));
}
/** Return the name of the TYPE that created the given operator. Return NULL if the operator is not found. */
public synchronized String getOperatorGenerationTypeName(OperatorSignature signature) {
RelvarOperators operatorsRelvar = (RelvarOperators)openGlobalRelvar(Catalog.relvarOperators);
return (operatorsRelvar.getOperatorGenerationTypeName(new Generator(RelDatabase.this, System.out), signature));
}
/** Create operator. */
public synchronized void createOperator(final Generator generator, final OperatorDefinition operator) {
if (isOperatorExists(operator.getSignature()))
throw new ExceptionSemantic("RS0216: OPERATOR " + operator.getSignature() + " is already in the database.");
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
RelvarOperators operatorsRelvar = (RelvarOperators)openGlobalRelvar(Catalog.relvarOperators);
String opsig = operator.getSignature().toRelLookupString();
String returnType = (operator.getDeclaredReturnType() != null) ? operator.getDeclaredReturnType().getSignature() : "";
operatorsRelvar.insertInternal(new Generator(RelDatabase.this, System.out), operator.getSignature().getName(), returnType, opsig, operator.getSourceCode(), operator.getLanguage(), operator.getCreatedByType(), operator.getOwner());
References references = operator.getReferences();
// Remove self-reference
references.removeReferenceToOperator(opsig);
addDependencies(generator, opsig, Catalog.relvarDependenciesOperatorOperator, references.getReferencedOperators());
addDependencies(generator, opsig, Catalog.relvarDependenciesOperatorRelvar, references.getReferencedRelvars());
addDependencies(generator, opsig, Catalog.relvarDependenciesOperatorType, references.getReferencedTypes());
recordDDL(generator, txn, operator.getSourceCode());
return null;
}
}).execute(this);
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0345: createOperator failed: " + t);
}
}
private void dropOperatorInternal(Generator generator, OperatorSignature signature) {
if (operatorCache.containsKey(signature))
operatorCache.remove(signature);
String opsig = signature.toRelLookupString();
StringBuffer dependencies = new StringBuffer();
obtainDependencies(generator, dependencies, opsig, Catalog.relvarDependenciesConstraintOperator, "CONSTRAINT");
obtainDependencies(generator, dependencies, opsig, Catalog.relvarDependenciesOperatorOperator, "OPERATOR");
obtainDependencies(generator, dependencies, opsig, Catalog.relvarDependenciesRelvarOperator, "VAR");
obtainDependencies(generator, dependencies, opsig, Catalog.relvarDependenciesTypeOperator, "TYPE");
if (dependencies.length() > 0)
throw new ExceptionSemantic("RS0217: Operator " + signature + " may not be dropped due to dependencies:" + dependencies);
RelvarOperators operatorsRelvar = (RelvarOperators)openGlobalRelvar(Catalog.relvarOperators);
operatorsRelvar.deleteInternal(generator, signature);
dirClassLoader.unload(getRelUserCodePackage() + "." + signature.getClassSignature());
removeDependencies(generator, opsig, Catalog.relvarDependenciesOperatorOperator);
removeDependencies(generator, opsig, Catalog.relvarDependenciesOperatorRelvar);
removeDependencies(generator, opsig, Catalog.relvarDependenciesOperatorType);
}
/** Drop operator. */
public synchronized void dropOperator(Generator generator, final OperatorSignature signature) {
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
dropOperatorInternal(new Generator(RelDatabase.this, System.out), signature);
recordDDL(generator, txn, "DROP OPERATOR " + signature.toRelLookupString() + ";");
return null;
}
}).execute(this);
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0346: dropOperator failed: " + t);
}
}
public Class<?> loadClass(String classSignature) {
try {
return dirClassLoader.forName(getRelUserCodePackage() + "." + classSignature);
} catch (ClassNotFoundException t) {
t.printStackTrace();
throw new ExceptionFatal("RS0347: Failed loading class " + classSignature + ": " + t.toString());
}
}
public Method getMethod(OperatorSignature signature) {
// Target class must have a 'static void execute(Context context)' method.
String classSignature = signature.getClassSignature();
try {
return loadClass(classSignature).getDeclaredMethod("execute", new Class[] {Context.class});
} catch (NoSuchMethodException m) {
m.printStackTrace();
throw new ExceptionFatal("RS0348: Failed loading operator " + signature + " from class " + classSignature + ": " + m.toString());
}
}
public void cacheOperator(OperatorDefinition op) {
operatorCache.put(op.getSignature(), op);
}
private OperatorDefinition getOperatorFromCache(OperatorSignature signature) {
OperatorDefinition definition = operatorCachePermanent.get(signature);
if (definition != null)
return definition;
return operatorCache.get(signature);
}
/** Return true if an operator of the given signature has been loaded. Return null if it can't be found. */
public synchronized OperatorDefinition loadOperator(Generator generator, OperatorSignature signature) {
OperatorDefinition definition = getOperatorFromCache(signature);
if (definition != null)
return definition;
definition = builtinops.get(signature.toNativeLookupString());
if (definition != null)
return definition;
RelvarOperators operatorsRelvar = (RelvarOperators)openGlobalRelvar(Catalog.relvarOperators);
ValueTuple operatorTuple = operatorsRelvar.getTupleForKey(generator, signature.getName());
if (operatorTuple == null)
return null;
Interpreter interpreter = new Interpreter(RelDatabase.this, System.out);
ValueRelationLiteral existingImplementations = (ValueRelationLiteral)operatorTuple.getValues()[1];
TupleIterator implementationIterator = existingImplementations.iterator();
try {
while (implementationIterator.hasNext()) {
ValueTuple implementationTuple = implementationIterator.next();
String existingImplementationSignature = ((ValueCharacter)implementationTuple.getValues()[0]).stringValue();
if (existingImplementationSignature.equalsIgnoreCase(signature.toRelLookupString())) {
String sourceCode = implementationTuple.getValues()[2].toString();
String language = implementationTuple.getValues()[3].toString();
if (language.equals("Rel") || language.equals("System")) {
try {
if (sourceCode.length() == 0) {
String createdByType = implementationTuple.getValues()[4].toString();
loadType(generator, createdByType);
return getOperatorFromCache(signature);
} else
return interpreter.compileOperator(sourceCode);
} catch (ParseException pe) {
pe.printStackTrace();
throw new ExceptionFatal("RS0349: Failed loading operator " + signature + ": " + pe.toString());
}
} else if (language.equals("JavaP")) {
return new OperatorDefinitionNativeProcedureExternal(signature, getMethod(signature));
} else if (language.equals("JavaF")) {
OperatorDefinitionNativeFunctionExternal opdef = new OperatorDefinitionNativeFunctionExternal(signature, getMethod(signature));
org.reldb.rel.v0.types.Type returnType;
try {
returnType = interpreter.getOperatorReturnType(sourceCode);
} catch (ParseException pe) {
pe.printStackTrace();
throw new ExceptionFatal("RS0350: Failed loading operator " + signature + ": " + pe.toString());
}
opdef.setDeclaredReturnType(returnType);
return opdef;
} else
throw new ExceptionFatal("RS0351: Unrecognised language '" + language + "' in operator " + signature);
}
}
} finally {
implementationIterator.close();
}
return null;
}
public synchronized void getPossibleTargetSignatures(HashSet<OperatorSignature> targets, Generator generator, OperatorSignature signature) {
RelvarOperators operatorsRelvar = (RelvarOperators)openGlobalRelvar(Catalog.relvarOperators);
ValueTuple operatorTuple = operatorsRelvar.getTupleForKey(generator, signature.getName());
if (operatorTuple != null) {
ValueRelationLiteral existingImplementations = (ValueRelationLiteral)operatorTuple.getValues()[1];
TupleIterator implementationIterator = existingImplementations.iterator();
try {
Interpreter interpreter = new Interpreter(RelDatabase.this, System.out);
while (implementationIterator.hasNext()) {
ValueTuple implementationTuple = implementationIterator.next();
String parmSignature = ((ValueCharacter)implementationTuple.getValues()[0]).stringValue();
String returnStr = ((ValueCharacter)implementationTuple.getValues()[1]).stringValue();
String existingImplementationSignatureText = parmSignature + ((returnStr.length() > 0) ? " RETURNS " + returnStr : "");
OperatorSignature storedSignature;
try {
storedSignature = interpreter.getOperatorSignature(existingImplementationSignatureText);
} catch (ParseException pe) {
pe.printStackTrace();
throw new ExceptionFatal("RS0352: Unable to retrieve operator signature for '" + existingImplementationSignatureText + "': " + pe.toString());
}
if (storedSignature.canBeInvokedBy(signature))
targets.add(storedSignature);
}
} finally {
implementationIterator.close();
}
}
for (OperatorDefinition op: operatorCache.values()) {
OperatorSignature storedSignature = op.getSignature();
if (storedSignature.canBeInvokedBy(signature))
targets.add(storedSignature);
}
OperatorDefinition definition = builtinops.get(signature.toNativeLookupString());
if (definition != null)
targets.add(definition.getSignature());
}
synchronized void defineBuiltinOperator(OperatorDefinition operator) {
builtinops.put(operator.getSignature().toNativeLookupString(), operator);
}
// Return list of built-in operators as RELATION {Name CHAR, Signature CHAR, ReturnsType CHAR, Specification CHAR}
public synchronized ValueRelation getBuiltinOperators(Generator generator) {
return new ValueRelation(generator) {
private static final long serialVersionUID = 1L;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
return new TupleIterator() {
Iterator<OperatorDefinition> i = builtinops.values().iterator();
public boolean hasNext() {
return i.hasNext();
}
public ValueTuple next() {
Generator generator = getGenerator();
OperatorDefinition operatorDefinition = i.next();
OperatorSignature signature = operatorDefinition.getSignature();
return new ValueTuple(generator, new Value[] {
ValueCharacter.select(generator, signature.getName()),
ValueCharacter.select(generator, signature.toRelLookupString()),
ValueCharacter.select(generator, (signature.getReturnType() != null) ? signature.getReturnType().getSignature() : ""),
ValueCharacter.select(generator, operatorDefinition.getSourceCode())
});
}
public void close() {}
};
}
};
}
/** Return true if a relvar exists. */
public synchronized boolean isRelvarExists(final String name) {
try {
return ((Boolean)(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
return new Boolean(isRelvarExists(txn, name));
}
}).execute(this)).booleanValue();
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0353: isRelvarExists failed: " + t);
}
}
/** Get the storage for a given real relvar name. */
public synchronized Storage getStorage(Transaction txn, String name) throws DatabaseException {
RelvarMetadata metadata = getRelvarMetadata(txn, name);
if (metadata == null)
return null;
if (!(metadata instanceof RelvarRealMetadata))
throw new ExceptionFatal("RS0354: VAR " + name + " is not a REAL relvar.");
StorageNames storageNames = ((RelvarRealMetadata)metadata).getStorageNames();
if (storageNames == null)
return null;
Storage storage = new Storage(storageNames.size());
for (int i=0; i<storageNames.size(); i++) {
String tabName = storageNames.getName(i);
Database db = openDatabase(txn, tabName, dbConfigurationNormal);
storage.setDatabase(i, db);
}
return storage;
}
// Retrieve a global Relvar's metadata. Return null if not found.
public RelvarMetadata getRelvarMetadata(final String relvarName) {
try {
return ((RelvarMetadata)(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
return getRelvarMetadata(txn, relvarName);
}
}).execute(this));
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0355: getRelvarMetadata failed: " + t);
}
}
private String getUniqueTableName() {
return "relvar_" + getUniqueID();
}
/** Create a real relvar with specified metadata. */
public synchronized void createRealRelvar(final Generator generator, final RelvarDefinition relvarInfo) {
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
if (isRelvarExists(txn, relvarInfo.getName()))
throw new ExceptionSemantic("RS0218: VAR " + relvarInfo.getName() + " already exists.");
RelvarMetadata metadata = getRelvarMetadata(txn, relvarInfo.getName());
if (metadata != null && metadata instanceof RelvarRealMetadata) {
StorageNames storageNames = ((RelvarRealMetadata)metadata).getStorageNames();
for (int i=0; i<storageNames.size(); i++) {
String tabName = storageNames.getName(i);
closeDatabase(tabName);
try {
environment.removeDatabase(txn, tabName);
} catch (DatabaseException dbe) {
dbe.printStackTrace();
throw new ExceptionFatal("RS0356: unable to remove table " + storageNames);
}
}
}
RelvarRealMetadata newMetadata = (RelvarRealMetadata)relvarInfo.getRelvarMetadata();
StorageNames storageNames = new StorageNames(newMetadata.getHeadingDefinition(RelDatabase.this).getKeyCount());
for (int i=0; i<storageNames.size(); i++) {
String tabName = getUniqueTableName();
storageNames.setName(i, tabName);
openDatabase(txn, tabName, dbConfigurationAllowCreate).close();
openStorage.remove(tabName);
}
newMetadata.setStorageNames(storageNames);
putRelvarMetadata(txn, relvarInfo.getName(), newMetadata);
addDependencies(generator, relvarInfo.getName(), Catalog.relvarDependenciesRelvarType, relvarInfo.getReferences().getReferencedTypes());
recordDDL(generator, txn, relvarInfo.toString());
return null;
}
}).execute(this);
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0357: createRealRelvar failed: " + t);
}
}
/** Create a VIRTUAL relvar. */
public synchronized void createVirtualRelvar(final Generator generator, final RelvarDefinition information) {
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
if (isRelvarExists(txn, information.getName()))
throw new ExceptionSemantic("RS0219: VAR " + information.getName() + " already exists.");
putRelvarMetadata(txn, information.getName(), information.getRelvarMetadata());
addDependencies(generator, information.getName(), Catalog.relvarDependenciesRelvarOperator, information.getReferences().getReferencedOperators());
addDependencies(generator, information.getName(), Catalog.relvarDependenciesRelvarRelvar, information.getReferences().getReferencedRelvars());
addDependencies(generator, information.getName(), Catalog.relvarDependenciesRelvarType, information.getReferences().getReferencedTypes());
recordDDL(generator, txn, information.toString());
return null;
}
}).execute(this);
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0358: createVirtualRelvar failed: " + t);
}
}
public void createExternalRelvar(final Generator generator, final RelvarDefinition relvarInfo) {
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
if (isRelvarExists(txn, relvarInfo.getName()))
throw new ExceptionSemantic("RS0220: VAR " + relvarInfo.getName() + " already exists.");
putRelvarMetadata(txn, relvarInfo.getName(), relvarInfo.getRelvarMetadata());
addDependencies(generator, relvarInfo.getName(), Catalog.relvarDependenciesRelvarType, relvarInfo.getReferences().getReferencedTypes());
recordDDL(generator, txn, relvarInfo.toString());
return null;
}
}).execute(this);
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0359: createExternalRelvar failed: " + t);
}
}
/** Get new temporary storage area. */
private synchronized Database createTempStorage(final DatabaseConfig config) {
String newTableName = getUniqueTableName();
Transaction txn = null;
Database database = openDatabase(txn, newTableName, config);
tempStorageNames.add(newTableName);
return database;
}
/** Get new temporary storage area. */
public Database createTempStorage() {
return createTempStorage(dbConfigurationTemporary);
}
/** Get new temporary storage area. Does not use custom comparator. */
public Database createTempStorageWithDuplicatesNoComparator() {
return createTempStorage(dbConfigurationTemporaryWithDuplicatesNoComparator);
}
/** Close temp storage */
public synchronized void destroyTempStorage(Database temp) {
String tableName = temp.getDatabaseName();
closeDatabase(tableName);
tempStorageNames.remove(tableName);
}
/** Get new 'private' (temporary) Table. */
public synchronized TablePrivate getTempTable(final RelvarHeading keyDefinition) {
try {
return (TablePrivate)((new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
Storage storage = new Storage(keyDefinition.getKeyCount());
for (int i=0; i<storage.size(); i++) {
String newTableName = getUniqueTableName();
Database db = openDatabase(txn, newTableName, dbConfigurationAllowCreate);
tempStorageNames.add(newTableName);
storage.setDatabase(i, db);
}
return new TablePrivate(RelDatabase.this, storage, keyDefinition);
}
}).execute(this));
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0360: getTempTable failed: " + t);
}
}
/** Create a PRIVATE relvar. */
public synchronized SlotScoped createPrivateRelvar(final int depth, final int offset, final RelvarHeading keydef) {
return new RelvarPrivate(depth, offset, this, keydef);
}
/** Open a global relvar. Return null if it doesn't exist. */
public synchronized RelvarGlobal openGlobalRelvar(final String name) {
try {
return ((RelvarGlobal)(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
RelvarMetadata metadata = getRelvarMetadata(txn, name);
if (metadata == null)
return null;
return metadata.getRelvar(name, RelDatabase.this);
}
}).execute(this));
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0361: openRelvar failed: " + t);
}
}
/** Drop a relvar. Throw an exception if it doesn't exist. */
public synchronized void dropRelvar(final String name) {
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
RelvarMetadata metadata = getRelvarMetadata(txn, name);
if (metadata == null)
throw new ExceptionSemantic("RS0221: VAR " + name + " does not exist.");
Generator generator = new Generator(RelDatabase.this, System.out);
StringBuffer dependencies = new StringBuffer();
obtainDependencies(generator, dependencies, name, Catalog.relvarDependenciesRelvarRelvar, "VAR");
obtainDependencies(generator, dependencies, name, Catalog.relvarDependenciesOperatorRelvar, "OPERATOR");
obtainDependencies(generator, dependencies, name, Catalog.relvarDependenciesConstraintRelvar, "CONSTRAINT");
obtainDependencies(generator, dependencies, name, Catalog.relvarDependenciesTypeRelvar, "TYPE");
if (dependencies.length() > 0)
throw new ExceptionSemantic("RS0222: VAR " + name + " may not be dropped due to dependencies:" + dependencies);
metadata.dropRelvar(RelDatabase.this);
if (metadata instanceof RelvarRealMetadata) {
StorageNames storageNames = ((RelvarRealMetadata)metadata).getStorageNames();
for (int i=0; i<storageNames.size(); i++) {
String tabName = storageNames.getName(i);
closeDatabase(tabName);
environment.removeDatabase(txn, tabName);
}
}
dropRelvarMetadata(txn, name);
removeDependencies(generator, name, Catalog.relvarDependenciesRelvarOperator);
removeDependencies(generator, name, Catalog.relvarDependenciesRelvarRelvar);
removeDependencies(generator, name, Catalog.relvarDependenciesRelvarType);
recordDDL(generator, txn, "DROP VAR " + name + ";");
return null;
}
}).execute(this);
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0362: dropRelvar failed: " + t);
}
}
// Copy tuples from a ValueRelation to a Table
private void copy(Generator generator, Transaction txn, Table table, Storage db, ValueRelation source) throws DatabaseException {
TupleIterator iterator = ((ValueRelation)source.getSerializableClone()).iterator();
try {
while (iterator.hasNext())
table.insertTupleNoDuplicates(generator, db, txn, iterator.next(), "Assigning");
} finally {
iterator.close();
}
}
// Set the value of a given Relvar
public synchronized void setValue(final RelvarPrivateCell target, final ValueRelation source) {
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
StorageNames newStorageNames = new StorageNames(target.getTable().getHeadingDefinition().getKeyCount());
Storage newDb = new Storage(newStorageNames.size());
for (int i=0; i<newStorageNames.size(); i++) {
String tabName = getUniqueTableName();
newStorageNames.setName(i, tabName);
Database db = openDatabase(txn, tabName, dbConfigurationAllowCreate);
tempStorageNames.add(tabName);
newDb.setDatabase(i, db);
}
copy(new Generator(RelDatabase.this, System.out), txn, target.getTable(), newDb, source);
Storage oldStorage = target.getTable().getStorage(txn);
for (int i=0; i<oldStorage.size(); i++) {
Database olddb = oldStorage.getDatabase(i);
String tabName = olddb.getDatabaseName();
openStorage.remove(tabName);
tempStorageNames.remove(tabName);
olddb.close();
environment.removeDatabase(txn, tabName);
}
target.setTable(newDb);
return null;
}
}).execute(this);
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0363: RelvarPrivate setValue failed: " + t);
}
}
// Set the value of a given Relvar
public synchronized void setValue(final RelvarReal target, final ValueRelation source) {
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
Storage oldStorage = getStorage(txn, target.getName());
StorageNames newStorageNames = new StorageNames(oldStorage.size());
Storage newStorage = new Storage(oldStorage.size());
for (int i=0; i<newStorageNames.size(); i++) {
String tabName = getUniqueTableName();
newStorageNames.setName(i, tabName);
Database dbase = openDatabase(txn, tabName, dbConfigurationAllowCreate);
newStorage.setDatabase(i, dbase);
}
copy(new Generator(RelDatabase.this, System.out), txn, target.getTable(), newStorage, source);
StorageNames oldStorageNames = oldStorage.getStorageNames();
for (int i=0; i<oldStorageNames.size(); i++) {
String tabName = oldStorageNames.getName(i);
openStorage.remove(tabName);
oldStorage.getDatabase(i).close();
environment.removeDatabase(txn, tabName);
}
RelvarRealMetadata metadata = (RelvarRealMetadata)getRelvarMetadata(txn, target.getName());
metadata.setStorageNames(newStorageNames);
putRelvarMetadata(txn, target.getName(), metadata);
return null;
}
}).execute(this);
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0364: RelvarReal setValue failed: " + t);
}
}
private abstract class Alteration {
public abstract void alter(Transaction txn, String varname, RelvarRealMetadata metadata);
}
private void alterVar(Generator generator, String varname, Alteration alteration) {
try {
(new TransactionRunner() {
public Object run(Transaction txn) throws Throwable {
RelvarMetadata rawMetadata = getRelvarMetadata(txn, varname);
if (rawMetadata == null)
throw new ExceptionSemantic("RS0430: REAL VAR '" + varname + "' does not exist.");
if (!(rawMetadata instanceof RelvarRealMetadata))
throw new ExceptionSemantic("RS0431: '" + varname + "' is not a REAL VAR.");
RelvarRealMetadata metadata = (RelvarRealMetadata)rawMetadata;
// perform specific alteration
alteration.alter(txn, varname, metadata);
// remove old metadata
dropRelvarMetadata(txn, varname);
// store new metadata
putRelvarMetadata(txn, varname, metadata);
return null;
}
}).execute(this);
} catch (ExceptionSemantic es) {
throw es;
} catch (Throwable t) {
t.printStackTrace();
throw new ExceptionFatal("RS0432: ALTER failed: " + t);
}
}
private void reindex(Transaction txn, Generator generator, String varname, RelvarRealMetadata metadata, RelvarHeading keydefs) {
// create new storage
StorageNames storageNames = new StorageNames(keydefs.getKeyCount());
Storage newStorage = new Storage(storageNames.size());
for (int i=0; i<storageNames.size(); i++) {
String tabName = getUniqueTableName();
storageNames.setName(i, tabName);
Database berkeleyDB = openDatabase(txn, tabName, dbConfigurationAllowCreate);
newStorage.setDatabase(i, berkeleyDB);
}
// copy old storage to new storage
RelvarReal relvar = (RelvarReal)metadata.getRelvar(varname, RelDatabase.this);
Table table = relvar.getTable();
TupleIterator iterator = relvar.iterator(generator);
try {
while (iterator.hasNext())
table.insertTupleNoDuplicates(generator, newStorage, txn, iterator.next(), "Altering KEY means");
} finally {
iterator.close();
}
// remove old storage
StorageNames oldStorageNames = ((RelvarRealMetadata)metadata).getStorageNames();
for (int i=0; i<oldStorageNames.size(); i++) {
String tabName = oldStorageNames.getName(i);
closeDatabase(tabName);
try {
environment.removeDatabase(txn, tabName);
} catch (DatabaseException dbe) {
dbe.printStackTrace();
throw new ExceptionFatal("RS0435: unable to remove table " + storageNames);
}
}
// set metadata to reference new storage names
metadata.setStorageNames(storageNames);
}
// Change type of an attribute
public synchronized void alterVarRealChangeAttributeType(Generator generator, String varname, String attributeName, Type newType) {
// System.out.println("alterVarRealChangeAttributeType: ALTER VAR " + varname + " TYPE_OF " + attributeName + " TO " + newType.getSignature());
alterVar(generator, varname, new Alteration() {
public void alter(Transaction txn, String varname, RelvarRealMetadata metadata) {
OperatorSignature signature = new OperatorSignature(newType.getSignature());
signature.addParameterType(TypeCharacter.getInstance());
OperatorInvocation newAttributeSelector = generator.findOperator(signature);
if (newAttributeSelector == null)
throw new ExceptionSemantic("RS0447: ALTER VAR " + varname + " TYPE_OF " + attributeName + " TO " + newType.getSignature() + " requires selector " + signature);
Instruction selector;
if (newAttributeSelector.useDynamicDispatch())
selector = new OpInvokeDynamicEvaluate(generator, newAttributeSelector.getOperatorSignature());
else
selector = new OpInvoke(newAttributeSelector.getStaticOperatorDefinition().getOperator());
RelvarReal relvar = (RelvarReal)metadata.getRelvar(varname, RelDatabase.this);
Table table = relvar.getTable();
Heading headingForConvertedAttribute = new Heading();
String tempName = "%tempname";
headingForConvertedAttribute.add(tempName, newType);
// update metadata to add new temporary attribute of new type
metadata.insertAttributes(RelDatabase.this, headingForConvertedAttribute);
// update each tuple to hold new attribute
ValueTuple newAttributes = new ValueTuple(generator, new TypeTuple(headingForConvertedAttribute));
table.expandTuples(txn, newAttributes);
// convert old attribute to new attribute
Heading heading = metadata.getHeadingDefinition(RelDatabase.this).getHeading();
int oldAttributeIndex = heading.getIndexOf(attributeName);
Type oldAttributeType = heading.getAttributes().get(oldAttributeIndex).getType();
int newAttributeIndex = heading.getIndexOf(tempName);
table.convertTuples(generator, txn, oldAttributeType, oldAttributeIndex, newAttributeIndex, selector);
// update metadata to drop old attribute
int attributeIndex = metadata.dropAttribute(RelDatabase.this, attributeName);
// update each tuple to remove attribute
table.shrinkTuples(txn, attributeIndex);
// rename temp attribute to old name
metadata.renameAttribute(RelDatabase.this, tempName, attributeName);
// reindex?
RelvarHeading keydefs = metadata.getHeadingDefinition(RelDatabase.this);
if (keydefs.isKeyUsing(attributeName))
reindex(txn, generator, varname, metadata, keydefs);
recordDDL(generator, txn, "ALTER VAR " + varname + " TYPE_OF " + attributeName + " TO " + newType.getSignature());
}
});
}
// ALTER VAR <varname> REAL ALTER KEY {...}
// Replace KEY definitions
public synchronized void alterVarRealAlterKey(Generator generator, String varname, RelvarHeading keydefs) {
// System.out.println("alterVarRealAlterKey: ALTER VAR " + varname + " " + keydefs.toString());
alterVar(generator, varname, new Alteration() {
public void alter(Transaction txn, String varname, RelvarRealMetadata metadata) {
// update metadata
metadata.setKeys(RelDatabase.this, keydefs);
// reindex
reindex(txn, generator, varname, metadata, keydefs);
recordDDL(generator, txn, "ALTER VAR " + varname + " " + keydefs.toString() + ";");
}
});
}
// ALTER VAR <varname> REAL DROP <attributename>
// Drop an attribute
public synchronized void alterVarRealDropAttribute(Generator generator, String varname, String attributeName) {
// System.out.println("alterVarRealDropAttribute: ALTER VAR " + varname + " DROP " + attributeName);
alterVar(generator, varname, new Alteration() {
public void alter(Transaction txn, String varname, RelvarRealMetadata metadata) {
// update metadata - get index within tuple of removed attribute
int attributeIndex = metadata.dropAttribute(RelDatabase.this, attributeName);
// update each tuple to remove attribute
RelvarReal relvar = (RelvarReal)metadata.getRelvar(varname, RelDatabase.this);
Table table = relvar.getTable();
table.shrinkTuples(txn, attributeIndex);
recordDDL(generator, txn, "ALTER VAR " + varname + " DROP " + attributeName);
}
});
}
// ALTER VAR <varname> REAL INSERT <attributename> <attributetype>
// Add an attribute
public synchronized void alterVarRealInsertAttributes(Generator generator, String varname, Heading heading) {
// System.out.println("alterVarRealInsertAttributes: ALTER VAR " + varname + " INSERT " + heading.toString());
alterVar(generator, varname, new Alteration() {
public void alter(Transaction txn, String varname, RelvarRealMetadata metadata) {
// update metadata
metadata.insertAttributes(RelDatabase.this, heading);
// update each tuple to hold new attributes
RelvarReal relvar = (RelvarReal)metadata.getRelvar(varname, RelDatabase.this);
Table table = relvar.getTable();
ValueTuple newAttributes = new ValueTuple(generator, new TypeTuple(heading));
table.expandTuples(txn, newAttributes);
recordDDL(generator, txn, "ALTER VAR " + varname + " INSERT " + heading.getSpecification() + ";");
}
});
}
// ALTER VAR <varname> REAL RENAME <oldattributename> TO <newattributename>
// Rename an attribute
public synchronized void alterVarRealRenameAttribute(Generator generator, String varname, String oldAttributeName, String newAttributeName) {
// System.out.println("alterVarRealRename: ALTER VAR " + varname + " RENAME " + oldAttributeName + " TO " + newAttributeName);
alterVar(generator, varname, new Alteration() {
public void alter(Transaction txn, String varname, RelvarRealMetadata metadata) {
// update metadata
metadata.renameAttribute(RelDatabase.this, oldAttributeName, newAttributeName);
recordDDL(generator, txn, "ALTER VAR " + varname + " RENAME " + oldAttributeName + " TO " + newAttributeName + ";");
}
});
}
}