📄 database.java
字号:
/* =============================================================
* SmallSQL : a free Java DBMS library for the Java(tm) platform
* =============================================================
*
* (C) Copyright 2004-2007, by Volker Berlin.
*
* Project Info: http://www.smallsql.de/
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* ---------------
* Database.java
* ---------------
* Author: Volker Berlin
*
*/
package smallsql.database;
import java.util.*;
import java.io.*;
import java.sql.*;
import smallsql.database.language.Language;
/**
* There are only one instance of this class per database. It will be share between all connections to this database and
* all threads. That the access must be thread safe.
*
* Here are save mainly table definitions and locks.
*/
final class Database{
static private HashMap databases = new HashMap();
final private TableViewMap tableViews = new TableViewMap();
private String name;
private File directory;
private RandomAccessFile master;
final private WeakHashMap connections = new WeakHashMap();
/**
* Get a instance of the Database Class. If the Datbase with the given name is not open
* then it will be open.
* @param name the name of the database
* @param con a referenz holder to this database, if all connection close that have a reference
* then the database can be unload.
* @param create if the database not exist then create it
*/
static Database getDatabase( String name, SSConnection con, boolean create ) throws SQLException{
if(name == null) return null;
if(name.startsWith("file:"))
name = name.substring(5);
if(File.separatorChar == '\\')
name = name.replace( '/', File.separatorChar);
else
name = name.replace( '\\', File.separatorChar);
synchronized(databases){
Database db = (Database)databases.get(name);
if(db == null){
if(create && !new File(name).isDirectory()){
CommandCreateDatabase command = new CommandCreateDatabase(con.log,name);
command.execute(con,null);
}
db = new Database(name);
databases.put( name, db);
}
db.connections.put(con, null);
return db;
}
}
private static Database getDatabase(SSConnection con, String name) throws SQLException{
return name == null ?
con.getDatabase(false) :
getDatabase( name, con, false );
}
private Database(String name ) throws SQLException{
try{
this.name = name;
directory = new File(name);
if(!directory.isDirectory()){
throw SmallSQLException.create(Language.DB_NONEXISTENT, name);
}
directory = directory.getAbsoluteFile();
File file = new File( directory, Utils.MASTER_FILENAME);
if(!file.exists())
throw SmallSQLException.create(Language.DB_NOT_DIRECTORY, name);
master = Utils.openRaFile(file);
}catch(Exception e){
throw SmallSQLException.createFromException(e);
}
}
String getName(){
return name;
}
/**
* Remove a connection from this database.
*/
static final void closeConnection(SSConnection con) throws SQLException{
synchronized(databases){
Iterator iterator = databases.values().iterator();
while(iterator.hasNext()){
Database database = (Database)iterator.next();
WeakHashMap connections = database.connections;
connections.remove(con);
if(connections.size() == 0){
try {
iterator.remove();
database.close();
} catch (Exception e) {
throw SmallSQLException.createFromException(e);
}
}
}
}
}
/**
* Close all tables and views of this Database.
*/
private final void close() throws Exception{
synchronized(tableViews){
Iterator iterator = tableViews.values().iterator();
while(iterator.hasNext()){
TableView tableView = (TableView)iterator.next();
tableView.close();
iterator.remove();
}
}
master.close();
}
static TableView getTableView(SSConnection con, String catalog, String tableName) throws SQLException{
return getDatabase( con, catalog).getTableView( con, tableName);
}
/**
* Return a TableView object. If the TableView object is not loaded then it load it.
* @param con
* @param tableName
* @return ever a valid TableView object and never null.
* @throws SQLException if the table or view does not exists
*/
TableView getTableView(SSConnection con, String tableName) throws SQLException{
synchronized(tableViews){
TableView tableView = tableViews.get(tableName);
if(tableView == null){
// FIXME it should block only one table and not all tables, loading of the table should outside of the global synchronized
tableView = TableView.load(con, this, tableName);
tableViews.put( tableName, tableView);
}
return tableView;
}
}
static void dropTable(SSConnection con, String catalog, String tableName) throws Exception{
getDatabase( con, catalog).dropTable( con, tableName);
}
void dropTable(SSConnection con, String tableName) throws Exception{
synchronized(tableViews){
Table table = (Table)tableViews.get( tableName );
if(table != null){
tableViews.remove( tableName );
table.drop(con);
}else{
Table.drop( this, tableName );
}
}
}
void replaceTable( Table oldTable, Table newTable) throws Exception{
synchronized(tableViews){
tableViews.remove( oldTable.name );
tableViews.remove( newTable.name );
oldTable.close();
newTable.close();
File oldFile = oldTable.getFile(this);
File newFile = newTable.getFile(this);
File tmpFile = new File(Utils.createTableViewFileName( this, "#" + System.currentTimeMillis() + this.hashCode() ));
if( !oldFile.renameTo(tmpFile) ){
throw SmallSQLException.create(Language.TABLE_CANT_RENAME, oldTable.name);
}
if( !newFile.renameTo(oldFile) ){
tmpFile.renameTo(oldFile); //restore the old table
throw SmallSQLException.create(Language.TABLE_CANT_RENAME, oldTable.name);
}
tmpFile.delete();
}
}
static void dropView(SSConnection con, String catalog, String tableName) throws Exception{
getDatabase( con, catalog).dropView(tableName);
}
void dropView(String viewName) throws Exception{
synchronized(tableViews){
Object view = tableViews.remove( viewName );
if(view != null && !(view instanceof View))
throw SmallSQLException.create(Language.VIEWDROP_NOT_VIEW, viewName);
View.drop( this, viewName );
}
}
private void checkForeignKeys( SSConnection con, ForeignKeys foreignKeys ) throws SQLException{
for(int i=0; i<foreignKeys.size(); i++){
ForeignKey foreignKey = foreignKeys.get(i);
TableView pkTable = getTableView(con, foreignKey.pkTable);
if(!(pkTable instanceof Table)){
throw SmallSQLException.create(Language.FK_NOT_TABLE, foreignKey.pkTable);
}
}
}
/**
* @param con current Connections
* @param name the name of the new Table
* @param columns the column descriptions of the table
* @param indexes the indexes of the new table
* @param foreignKeys
* @throws Exception
*/
void createTable(SSConnection con, String name, Columns columns, IndexDescriptions indexes, ForeignKeys foreignKeys) throws Exception{
checkForeignKeys( con, foreignKeys );
// createFile() can run only one Thread success (it is atomic)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -