0001: /**
0002: * com.mckoi.database.Database 02 Mar 1998
0003: *
0004: * Mckoi SQL Database ( http://www.mckoi.com/database )
0005: * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License
0009: * Version 2 as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0014: * GNU General Public License Version 2 for more details.
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * Version 2 along with this program; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0019: *
0020: * Change Log:
0021: *
0022: *
0023: */package com.mckoi.database;
0024:
0025: import java.sql.*;
0026: import java.io.File;
0027: import java.io.PrintStream;
0028: import java.io.IOException;
0029: import java.util.ArrayList;
0030: import java.util.Map;
0031: import com.mckoi.debug.*;
0032: import com.mckoi.util.Log;
0033: import com.mckoi.util.Stats;
0034: import com.mckoi.util.Cache;
0035: import com.mckoi.database.global.*;
0036: import com.mckoi.database.control.DefaultDBConfig;
0037: import com.mckoi.database.jdbc.MSQLException;
0038: import com.mckoi.store.Store;
0039: import com.mckoi.store.MutableArea;
0040:
0041: /**
0042: * The representation of a single database in the system. A database
0043: * is a set of schema, a set of tables and table definitions of tables in
0044: * the schema, and a description of the schema.
0045: * <p>
0046: * This class encapsulates the top level behaviour of a database. That is
0047: * of creating itself, initializing itself, shutting itself down, deleting
0048: * itself, creating/dropping a table, updating a table. It is not the
0049: * responsibility of this class to handle table behaviour above this. Top
0050: * level table behaviour is handled by DataTable through the DatabaseConnection
0051: * interface.
0052: * <p>
0053: * The Database object is also responsible for various database management
0054: * functions such a creating, editing and removing users, triggers, functions
0055: * and services.
0056: *
0057: * @author Tobias Downer
0058: */
0059:
0060: public final class Database implements DatabaseConstants {
0061:
0062: // ---------- Statics ----------
0063:
0064: /**
0065: * The name of the system schema that contains tables refering to system
0066: * information.
0067: */
0068: public static final String SYSTEM_SCHEMA = TableDataConglomerate.SYSTEM_SCHEMA;
0069:
0070: /**
0071: * The name of the default schema.
0072: */
0073: public static final String DEFAULT_SCHEMA = "APP";
0074:
0075: /**
0076: * The name of the schema that contains JDBC helper tables.
0077: */
0078: public static final String JDBC_SCHEMA = "SYS_JDBC";
0079:
0080: /**
0081: * The password privs and grants table.
0082: */
0083: public static final TableName SYS_PASSWORD = new TableName(
0084: SYSTEM_SCHEMA, "sUSRPassword");
0085:
0086: public static final TableName SYS_USERCONNECT = new TableName(
0087: SYSTEM_SCHEMA, "sUSRUserConnectPriv");
0088:
0089: public static final TableName SYS_USERPRIV = new TableName(
0090: SYSTEM_SCHEMA, "sUSRUserPriv");
0091:
0092: public static final TableName SYS_GRANTS = new TableName(
0093: SYSTEM_SCHEMA, "sUSRGrant");
0094:
0095: /**
0096: * The services table.
0097: */
0098: public static final TableName SYS_SERVICE = new TableName(
0099: SYSTEM_SCHEMA, "sUSRService");
0100:
0101: /**
0102: * The function factory table.
0103: */
0104: public static final TableName SYS_FUNCTIONFACTORY = new TableName(
0105: SYSTEM_SCHEMA, "sUSRFunctionFactory");
0106:
0107: /**
0108: * The function table.
0109: */
0110: public static final TableName SYS_FUNCTION = new TableName(
0111: SYSTEM_SCHEMA, "sUSRFunction");
0112:
0113: /**
0114: * The view table.
0115: */
0116: public static final TableName SYS_VIEW = new TableName(
0117: SYSTEM_SCHEMA, "sUSRView");
0118:
0119: /**
0120: * The label table.
0121: */
0122: public static final TableName SYS_LABEL = new TableName(
0123: SYSTEM_SCHEMA, "sUSRLabel");
0124:
0125: /**
0126: * The system internally generated 'sUSRTableColumns' table.
0127: */
0128: public static final TableName SYS_TABLE_COLUMNS = new TableName(
0129: SYSTEM_SCHEMA, "sUSRTableColumns");
0130:
0131: /**
0132: * The system internally generated 'sUSRTableInfo' table.
0133: */
0134: public static final TableName SYS_TABLE_INFO = new TableName(
0135: SYSTEM_SCHEMA, "sUSRTableInfo");
0136:
0137: /**
0138: * The system internally generated 'sUSRDataTrigger' table.
0139: */
0140: public static final TableName SYS_DATA_TRIGGER = new TableName(
0141: SYSTEM_SCHEMA, "sUSRDataTrigger");
0142:
0143: /**
0144: * The system internally generated 'sUSRDatabaseStatistics' table.
0145: */
0146: public static final TableName SYS_DB_STATISTICS = new TableName(
0147: SYSTEM_SCHEMA, "sUSRDatabaseStatistics");
0148:
0149: /**
0150: * The OLD table used inside a triggered procedure to represent a triggered
0151: * row before the operation occurs.
0152: */
0153: public static final TableName OLD_TRIGGER_TABLE = new TableName(
0154: SYSTEM_SCHEMA, "OLD");
0155:
0156: /**
0157: * The NEW table used inside a triggered procedure to represent a triggered
0158: * row after the operation occurs.
0159: */
0160: public static final TableName NEW_TRIGGER_TABLE = new TableName(
0161: SYSTEM_SCHEMA, "NEW");
0162:
0163: /**
0164: * The name of the lock group. If a user belongs to this group the user
0165: * account is locked and they are not allowed to log into the database.
0166: */
0167: public static final String LOCK_GROUP = "#locked";
0168:
0169: /**
0170: * THe name of the secure access group. If a user belongs to this group they
0171: * are permitted to perform a number of priviledged operations such as
0172: * shutting down the database, and adding and removing users.
0173: */
0174: public static final String SECURE_GROUP = "secure access";
0175:
0176: /**
0177: * The name of the user manager group. Users that belong in this group can
0178: * create, alter and drop users from the system.
0179: */
0180: public static final String USER_MANAGER_GROUP = "user manager";
0181:
0182: /**
0183: * The name of the schema manager group. Users that belong in this group can
0184: * create and drop schema from the system.
0185: */
0186: public static final String SCHEMA_MANAGER_GROUP = "schema manager";
0187:
0188: /**
0189: * The username of the internal secure user. The internal secure user is only
0190: * used for internal highly privileged operations. This user is given full
0191: * privs to everything and is used to manage the system tables, for
0192: * authentication, etc.
0193: */
0194: public static final String INTERNAL_SECURE_USERNAME = "@SYSTEM";
0195:
0196: // ---------- Members ----------
0197:
0198: /**
0199: * The DatabaseSystem that this database is part of.
0200: */
0201: private DatabaseSystem system;
0202:
0203: /**
0204: * The name of this database.
0205: */
0206: private String name;
0207:
0208: /**
0209: * The TableDataConglomerate that contains the conglomerate of tables for
0210: * this database.
0211: */
0212: private TableDataConglomerate conglomerate;
0213:
0214: /**
0215: * A flag which, when set to true, will cause the engine to delete the
0216: * database from the file system when it is shut down.
0217: */
0218: private boolean delete_on_shutdown;
0219:
0220: /**
0221: * An internal secure User that is given full grant access to the entire
0222: * database. This user is used to execute system level queries such as
0223: * creating and updating system tables.
0224: */
0225: private User internal_system_user;
0226:
0227: /**
0228: * The database wide TriggerManager object that dispatches trigger events
0229: * to the DatabaseConnection objects that are listening for the events.
0230: */
0231: private TriggerManager trigger_manager;
0232:
0233: /**
0234: * The various log files.
0235: */
0236: /**
0237: * This log file records the DQL commands executed on the server.
0238: */
0239: private Log commands_log;
0240:
0241: /**
0242: * This is set to true when the 'init()' method is first called.
0243: */
0244: private boolean initialised = false;
0245:
0246: /**
0247: * A table that has a single row but no columns.
0248: */
0249: private final Table SINGLE_ROW_TABLE;
0250:
0251: /**
0252: * The Constructor. This takes a directory path in which the database is
0253: * stored.
0254: */
0255: public Database(DatabaseSystem system, String name) {
0256: this .system = system;
0257: this .delete_on_shutdown = false;
0258: this .name = name;
0259: conglomerate = new TableDataConglomerate(system, system
0260: .storeSystem());
0261: internal_system_user = new User(INTERNAL_SECURE_USERNAME, this ,
0262: "", System.currentTimeMillis());
0263:
0264: // Create the single row table
0265: TemporaryTable t;
0266: t = new TemporaryTable(this , "SINGLE_ROW_TABLE",
0267: new DataTableColumnDef[0]);
0268: t.newRow();
0269: SINGLE_ROW_TABLE = t;
0270:
0271: trigger_manager = new TriggerManager(system);
0272:
0273: }
0274:
0275: /**
0276: * Returns the name of this database.
0277: */
0278: public String getName() {
0279: return name;
0280: }
0281:
0282: /**
0283: * Returns true if this database is in read only mode.
0284: */
0285: public boolean isReadOnly() {
0286: return getSystem().readOnlyAccess();
0287: }
0288:
0289: /**
0290: * Returns the internal system user for this database.
0291: */
0292: private User internalSystemUser() {
0293: return internal_system_user;
0294: }
0295:
0296: // ---------- Log accesses ----------
0297:
0298: /**
0299: * Returns the log file where commands are recorded.
0300: */
0301: public Log getCommandsLog() {
0302: return commands_log;
0303: }
0304:
0305: /**
0306: * Returns the conglomerate for this database.
0307: */
0308: TableDataConglomerate getConglomerate() {
0309: return conglomerate;
0310: }
0311:
0312: /**
0313: * Returns a new DatabaseConnection instance that is used against this
0314: * database.
0315: * <p>
0316: * When a new connection is made on this database, this method is called
0317: * to create a new DatabaseConnection instance for the connection. This
0318: * connection handles all transactional queries and modifications to the
0319: * database.
0320: */
0321: public DatabaseConnection createNewConnection(User user,
0322: DatabaseConnection.CallBack call_back) {
0323: if (user == null) {
0324: user = internalSystemUser();
0325: }
0326:
0327: DatabaseConnection connection = new DatabaseConnection(this ,
0328: user, call_back);
0329: // Initialize the connection
0330: connection.init();
0331:
0332: return connection;
0333: }
0334:
0335: // ---------- Database user management functions ----------
0336:
0337: /**
0338: * Tries to authenticate a username/password against this database. If we
0339: * fail to authenticate then a 'null' object is returned, otherwise a valid
0340: * User object is returned. If a valid object is returned, the user
0341: * will be logged into the engine via the UserManager object (in
0342: * DatabaseSystem). The developer must ensure that 'close' is called before
0343: * the object is disposed (logs out of the system).
0344: * <p>
0345: * This method also returns null if a user exists but was denied access from
0346: * the given host string. The given 'host_name' object is formatted in the
0347: * database host connection encoding. This method checks all the values
0348: * from the sUSRUserConnectPriv table for this user for the given protocol.
0349: * It first checks if the user is specifically DENIED access from the given
0350: * host. It then checks if the user is ALLOWED access from the given host.
0351: * If a host is neither allowed or denied then it is denied.
0352: */
0353: public User authenticateUser(String username, String password,
0354: String connection_string) {
0355:
0356: // Create a temporary connection for authentication only...
0357: DatabaseConnection connection = createNewConnection(null, null);
0358: DatabaseQueryContext context = new DatabaseQueryContext(
0359: connection);
0360: connection.setCurrentSchema(SYSTEM_SCHEMA);
0361: LockingMechanism locker = connection.getLockingMechanism();
0362: locker.setMode(LockingMechanism.EXCLUSIVE_MODE);
0363: try {
0364:
0365: try {
0366: Connection jdbc = connection.getJDBCConnection();
0367:
0368: // Is the username/password in the database?
0369: PreparedStatement stmt = jdbc
0370: .prepareStatement(" SELECT \"UserName\" FROM \"sUSRPassword\" "
0371: + " WHERE \"sUSRPassword.UserName\" = ? "
0372: + " AND \"sUSRPassword.Password\" = ? ");
0373: stmt.setString(1, username);
0374: stmt.setString(2, password);
0375: ResultSet rs = stmt.executeQuery();
0376: if (!rs.next()) {
0377: return null;
0378: }
0379: rs.close();
0380: stmt.close();
0381:
0382: // Now check if this user is permitted to connect from the given
0383: // host.
0384: if (userAllowedAccessFromHost(context, username,
0385: connection_string)) {
0386: // Successfully authenticated...
0387: User user = new User(username, this ,
0388: connection_string, System
0389: .currentTimeMillis());
0390: // Log the authenticated user in to the engine.
0391: system.getUserManager().userLoggedIn(user);
0392: return user;
0393: }
0394:
0395: return null;
0396:
0397: } catch (SQLException e) {
0398: if (e instanceof MSQLException) {
0399: MSQLException msqle = (MSQLException) e;
0400: Debug().write(Lvl.ERROR, this ,
0401: msqle.getServerErrorStackTrace());
0402: }
0403: Debug().writeException(Lvl.ERROR, e);
0404: throw new RuntimeException("SQL Error: "
0405: + e.getMessage());
0406: }
0407:
0408: } finally {
0409: try {
0410: // Make sure we commit the connection.
0411: connection.commit();
0412: } catch (TransactionException e) {
0413: // Just issue a warning...
0414: Debug().writeException(Lvl.WARNING, e);
0415: } finally {
0416: // Guarentee that we unluck from EXCLUSIVE
0417: locker.finishMode(LockingMechanism.EXCLUSIVE_MODE);
0418: }
0419: // And make sure we close (dispose) of the temporary connection.
0420: connection.close();
0421: }
0422:
0423: }
0424:
0425: /**
0426: * Performs check to determine if user is allowed access from the given
0427: * host. See the comments of 'authenticateUser' for a description of
0428: * how this is determined.
0429: */
0430: private boolean userAllowedAccessFromHost(
0431: DatabaseQueryContext context, String username,
0432: String connection_string) {
0433:
0434: // The system user is not allowed to login
0435: if (username.equals(INTERNAL_SECURE_USERNAME)) {
0436: return false;
0437: }
0438:
0439: // We always allow access from 'Internal/*' (connections from the
0440: // 'getConnection' method of a com.mckoi.database.control.DBSystem object)
0441: // ISSUE: Should we add this as a rule?
0442: if (connection_string.startsWith("Internal/")) {
0443: return true;
0444: }
0445:
0446: // What's the protocol?
0447: int protocol_host_deliminator = connection_string.indexOf("/");
0448: String protocol = connection_string.substring(0,
0449: protocol_host_deliminator);
0450: String host = connection_string
0451: .substring(protocol_host_deliminator + 1);
0452:
0453: if (Debug().isInterestedIn(Lvl.INFORMATION)) {
0454: Debug().write(
0455: Lvl.INFORMATION,
0456: this ,
0457: "Checking host: protocol = " + protocol
0458: + ", host = " + host);
0459: }
0460:
0461: // The table to check
0462: DataTable connect_priv = context.getTable(SYS_USERCONNECT);
0463: Variable un_col = connect_priv.getResolvedVariable(0);
0464: Variable proto_col = connect_priv.getResolvedVariable(1);
0465: Variable host_col = connect_priv.getResolvedVariable(2);
0466: Variable access_col = connect_priv.getResolvedVariable(3);
0467: // Query: where UserName = %username%
0468: Table t = connect_priv.simpleSelect(context, un_col, Operator
0469: .get("="), new Expression(TObject.stringVal(username)));
0470: // Query: where %protocol% like Protocol
0471: Expression exp = Expression.simple(TObject.stringVal(protocol),
0472: Operator.get("like"), proto_col);
0473: t = t.exhaustiveSelect(context, exp);
0474: // Query: where %host% like Host
0475: exp = Expression.simple(TObject.stringVal(host), Operator
0476: .get("like"), host_col);
0477: t = t.exhaustiveSelect(context, exp);
0478:
0479: // Those that are DENY
0480: Table t2 = t.simpleSelect(context, access_col, Operator
0481: .get("="), new Expression(TObject.stringVal("DENY")));
0482: if (t2.getRowCount() > 0) {
0483: return false;
0484: }
0485: // Those that are ALLOW
0486: Table t3 = t.simpleSelect(context, access_col, Operator
0487: .get("="), new Expression(TObject.stringVal("ALLOW")));
0488: if (t3.getRowCount() > 0) {
0489: return true;
0490: }
0491: // No DENY or ALLOW entries for this host so deny access.
0492: return false;
0493:
0494: }
0495:
0496: /**
0497: * Returns true if a user exists in this database, otherwise returns
0498: * false.
0499: * <p>
0500: * NOTE: Assumes exclusive lock on DatabaseConnection.
0501: */
0502: public boolean userExists(DatabaseQueryContext context,
0503: String username) throws DatabaseException {
0504: DataTable table = context.getTable(SYS_PASSWORD);
0505: Variable c1 = table.getResolvedVariable(0);
0506: // All sUSRPassword where UserName = %username%
0507: Table t = table.simpleSelect(context, c1, Operator.get("="),
0508: new Expression(TObject.stringVal(username)));
0509: return t.getRowCount() > 0;
0510: }
0511:
0512: /**
0513: * Creates and adds a new user to this database. The User object for
0514: * the user is returned.
0515: * <p>
0516: * If the user is already defined by the database then an error is generated.
0517: * <p>
0518: * NOTE: Assumes exclusive lock on DatabaseConnection.
0519: */
0520: public void createUser(DatabaseQueryContext context,
0521: String username, String password) throws DatabaseException {
0522:
0523: if (username == null || password == null) {
0524: throw new DatabaseException(
0525: "Username or password can not be NULL.");
0526: }
0527:
0528: // The username must be more than 1 character
0529: if (username.length() <= 1) {
0530: throw new DatabaseException(
0531: "Username must be at least 2 characters.");
0532: }
0533:
0534: // The password must be more than 1 character
0535: if (password.length() <= 1) {
0536: throw new DatabaseException(
0537: "Password must be at least 2 characters.");
0538: }
0539:
0540: // Check the user doesn't already exist
0541: if (userExists(context, username)) {
0542: throw new DatabaseException("User '" + username
0543: + "' already exists.");
0544: }
0545:
0546: // Some usernames are reserved words
0547: if (username.equalsIgnoreCase("public")) {
0548: throw new DatabaseException("User '" + username
0549: + "' not allowed - reserved.");
0550: }
0551:
0552: // Usernames starting with @, &, # and $ are reserved for system
0553: // identifiers
0554: char c = username.charAt(0);
0555: if (c == '@' || c == '&' || c == '#' || c == '$') {
0556: throw new DatabaseException(
0557: "User name can not start with '" + c
0558: + "' character.");
0559: }
0560:
0561: // Add this user to the password table.
0562: DataTable table = context.getTable(SYS_PASSWORD);
0563: RowData rdat = new RowData(table);
0564: rdat.setColumnDataFromObject(0, username);
0565: rdat.setColumnDataFromObject(1, password);
0566: table.add(rdat);
0567:
0568: }
0569:
0570: /**
0571: * Deletes all the groups the user belongs to. This is intended for a user
0572: * alter command for setting the groups a user belongs to.
0573: * <p>
0574: * NOTE: Assumes exclusive lock on DatabaseConnection.
0575: */
0576: public void deleteAllUserGroups(DatabaseQueryContext context,
0577: String username) throws DatabaseException {
0578: Operator EQUALS_OP = Operator.get("=");
0579: Expression USER_EXPR = new Expression(TObject
0580: .stringVal(username));
0581:
0582: DataTable table = context.getTable(SYS_USERPRIV);
0583: Variable c1 = table.getResolvedVariable(0);
0584: // All sUSRUserPriv where UserName = %username%
0585: Table t = table.simpleSelect(context, c1, EQUALS_OP, USER_EXPR);
0586: // Delete all the groups
0587: table.delete(t);
0588:
0589: }
0590:
0591: /**
0592: * Deletes the user from the system. This also deletes all information
0593: * associated with a user such as the groups they belong to. It does not
0594: * delete the privs a user has set up.
0595: * <p>
0596: * NOTE: Assumes exclusive lock on DatabaseConnection.
0597: */
0598: public void deleteUser(DatabaseQueryContext context, String username)
0599: throws DatabaseException {
0600: // PENDING: This should check if there are any tables the user has setup
0601: // and not allow the delete if there are.
0602:
0603: Operator EQUALS_OP = Operator.get("=");
0604: Expression USER_EXPR = new Expression(TObject
0605: .stringVal(username));
0606:
0607: // First delete all the groups from the user priv table
0608: deleteAllUserGroups(context, username);
0609:
0610: // Now delete the username from the sUSRUserConnectPriv table
0611: DataTable table = context.getTable(SYS_USERCONNECT);
0612: Variable c1 = table.getResolvedVariable(0);
0613: Table t = table.simpleSelect(context, c1, EQUALS_OP, USER_EXPR);
0614: table.delete(t);
0615:
0616: // Finally delete the username from the sUSRPassword table
0617: table = context.getTable(SYS_PASSWORD);
0618: c1 = table.getResolvedVariable(0);
0619: t = table.simpleSelect(context, c1, EQUALS_OP, USER_EXPR);
0620: table.delete(t);
0621:
0622: }
0623:
0624: /**
0625: * Alters the password of the user but otherwise does not change any
0626: * information about the user.
0627: * <p>
0628: * NOTE: Assumes exclusive lock on DatabaseConnection.
0629: */
0630: public void alterUserPassword(DatabaseQueryContext context,
0631: String username, String password) throws DatabaseException {
0632:
0633: Operator EQUALS_OP = Operator.get("=");
0634: Expression USER_EXPR = new Expression(TObject
0635: .stringVal(username));
0636:
0637: // Delete the current username from the sUSRPassword table
0638: DataTable table = context.getTable(SYS_PASSWORD);
0639: Variable c1 = table.getResolvedVariable(0);
0640: Table t = table.simpleSelect(context, c1, EQUALS_OP, USER_EXPR);
0641: if (t.getRowCount() == 1) {
0642: table.delete(t);
0643:
0644: // Add the new username
0645: table = context.getTable(SYS_PASSWORD);
0646: RowData rdat = new RowData(table);
0647: rdat.setColumnDataFromObject(0, username);
0648: rdat.setColumnDataFromObject(1, password);
0649: table.add(rdat);
0650:
0651: } else {
0652: throw new DatabaseException("Username '" + username
0653: + "' was not found.");
0654: }
0655:
0656: }
0657:
0658: /**
0659: * Returns the list of all user groups the user belongs to.
0660: */
0661: public String[] groupsUserBelongsTo(DatabaseQueryContext context,
0662: String username) throws DatabaseException {
0663:
0664: DataTable table = context.getTable(SYS_USERPRIV);
0665: Variable c1 = table.getResolvedVariable(0);
0666: // All sUSRUserPriv where UserName = %username%
0667: Table t = table.simpleSelect(context, c1, Operator.get("="),
0668: new Expression(TObject.stringVal(username)));
0669: int sz = t.getRowCount();
0670: String[] groups = new String[sz];
0671: RowEnumeration row_enum = t.rowEnumeration();
0672: int i = 0;
0673: while (row_enum.hasMoreRows()) {
0674: groups[i] = t.getCellContents(1, row_enum.nextRowIndex())
0675: .getObject().toString();
0676: ++i;
0677: }
0678:
0679: return groups;
0680: }
0681:
0682: /**
0683: * Returns true if the given user belongs to the given group otherwise
0684: * returns false.
0685: * <p>
0686: * NOTE: Assumes exclusive lock on DatabaseConnection.
0687: */
0688: public boolean userBelongsToGroup(DatabaseQueryContext context,
0689: String username, String group) throws DatabaseException {
0690:
0691: DataTable table = context.getTable(SYS_USERPRIV);
0692: Variable c1 = table.getResolvedVariable(0);
0693: Variable c2 = table.getResolvedVariable(1);
0694: // All sUSRUserPriv where UserName = %username%
0695: Table t = table.simpleSelect(context, c1, Operator.get("="),
0696: new Expression(TObject.stringVal(username)));
0697: // All from this set where PrivGroupName = %group%
0698: t = t.simpleSelect(context, c2, Operator.get("="),
0699: new Expression(TObject.stringVal(group)));
0700: return t.getRowCount() > 0;
0701: }
0702:
0703: /**
0704: * Adds the user to the given group. This makes an entry in the sUSRUserPriv
0705: * for this user and the given group. If the user already belongs to the
0706: * group then no changes are made.
0707: * <p>
0708: * It is important that any security checks for ensuring the grantee is
0709: * allowed to give the user these privs are preformed before this method is
0710: * called.
0711: * <p>
0712: * NOTE: Assumes exclusive lock on DatabaseConnection.
0713: */
0714: public void addUserToGroup(DatabaseQueryContext context,
0715: String username, String group) throws DatabaseException {
0716: if (group == null) {
0717: throw new DatabaseException("Can add NULL group.");
0718: }
0719:
0720: // Groups starting with @, &, # and $ are reserved for system
0721: // identifiers
0722: char c = group.charAt(0);
0723: if (c == '@' || c == '&' || c == '#' || c == '$') {
0724: throw new DatabaseException(
0725: "The group name can not start with '" + c
0726: + "' character.");
0727: }
0728:
0729: // Check the user doesn't belong to the group
0730: if (!userBelongsToGroup(context, username, group)) {
0731: // The user priv table
0732: DataTable table = context.getTable(SYS_USERPRIV);
0733: // Add this user to the group.
0734: RowData rdat = new RowData(table);
0735: rdat.setColumnDataFromObject(0, username);
0736: rdat.setColumnDataFromObject(1, group);
0737: table.add(rdat);
0738: }
0739: // NOTE: we silently ignore the case when a user already belongs to the
0740: // group.
0741: }
0742:
0743: /**
0744: * Sets the lock status for the given user. If a user account if locked, it
0745: * is rejected from logging in to the database.
0746: * <p>
0747: * It is important that any security checks to determine if the process
0748: * setting the user lock is allowed to do it is done before this method is
0749: * called.
0750: * <p>
0751: * NOTE: Assumes exclusive lock on DatabaseConnection.
0752: */
0753: public void setUserLock(DatabaseQueryContext context, User user,
0754: boolean lock_status) throws DatabaseException {
0755:
0756: String username = user.getUserName();
0757:
0758: // Internally we implement this by adding the user to the #locked group.
0759: DataTable table = context.getTable(SYS_USERPRIV);
0760: Variable c1 = table.getResolvedVariable(0);
0761: Variable c2 = table.getResolvedVariable(1);
0762: // All sUSRUserPriv where UserName = %username%
0763: Table t = table.simpleSelect(context, c1, Operator.get("="),
0764: new Expression(TObject.stringVal(username)));
0765: // All from this set where PrivGroupName = %group%
0766: t = t.simpleSelect(context, c2, Operator.get("="),
0767: new Expression(TObject.stringVal(LOCK_GROUP)));
0768:
0769: boolean user_belongs_to_lock_group = t.getRowCount() > 0;
0770: if (lock_status && !user_belongs_to_lock_group) {
0771: // Lock the user by adding the user to the lock group
0772: // Add this user to the locked group.
0773: RowData rdat = new RowData(table);
0774: rdat.setColumnDataFromObject(0, username);
0775: rdat.setColumnDataFromObject(1, LOCK_GROUP);
0776: table.add(rdat);
0777: } else if (!lock_status && user_belongs_to_lock_group) {
0778: // Unlock the user by removing the user from the lock group
0779: // Remove this user from the locked group.
0780: table.delete(t);
0781: }
0782:
0783: }
0784:
0785: /**
0786: * Grants the given user access to connect to the database from the
0787: * given host address. The 'protocol' string is the connecting protocol
0788: * which can be either 'TCP' or 'Local'. The 'host' string is the actual
0789: * host that is connecting. For example, if the protocol was TCP then
0790: * the client host may be '127.0.0.1' for localhost.
0791: */
0792: public void grantHostAccessToUser(DatabaseQueryContext context,
0793: String user, String protocol, String host)
0794: throws DatabaseException {
0795:
0796: // The user connect priv table
0797: DataTable table = context.getTable(SYS_USERCONNECT);
0798: // Add the protocol and host to the table
0799: RowData rdat = new RowData(table);
0800: rdat.setColumnDataFromObject(0, user);
0801: rdat.setColumnDataFromObject(1, protocol);
0802: rdat.setColumnDataFromObject(2, host);
0803: rdat.setColumnDataFromObject(3, "ALLOW");
0804: table.add(rdat);
0805:
0806: }
0807:
0808: /**
0809: * Returns true if the user belongs to the secure access priv group.
0810: */
0811: private boolean userHasSecureAccess(DatabaseQueryContext context,
0812: User user) throws DatabaseException {
0813: // The internal secure user has full privs on everything
0814: if (user.getUserName().equals(INTERNAL_SECURE_USERNAME)) {
0815: return true;
0816: }
0817: return userBelongsToGroup(context, user.getUserName(),
0818: SECURE_GROUP);
0819: }
0820:
0821: /**
0822: * Returns true if the grant manager permits a schema operation (eg,
0823: * CREATE, ALTER and DROP table operations) for the given user.
0824: */
0825: private boolean userHasSchemaGrant(DatabaseQueryContext context,
0826: User user, String schema, int grant)
0827: throws DatabaseException {
0828: // The internal secure user has full privs on everything
0829: if (user.getUserName().equals(INTERNAL_SECURE_USERNAME)) {
0830: return true;
0831: }
0832:
0833: // No users have schema access to the system schema.
0834: if (schema.equals(SYSTEM_SCHEMA)) {
0835: return false;
0836: }
0837:
0838: // Ask the grant manager if there are any privs setup for this user on the
0839: // given schema.
0840: GrantManager manager = context.getGrantManager();
0841: Privileges privs = manager.userGrants(GrantManager.SCHEMA,
0842: schema, user.getUserName());
0843:
0844: return privs.permits(grant);
0845: }
0846:
0847: /**
0848: * Returns true if the grant manager permits a table object operation (eg,
0849: * SELECT, INSERT, UPDATE, DELETE and COMPACT table operations) for the given
0850: * user.
0851: */
0852: private boolean userHasTableObjectGrant(
0853: DatabaseQueryContext context, User user,
0854: TableName table_name, Variable[] columns, int grant)
0855: throws DatabaseException {
0856:
0857: // The internal secure user has full privs on everything
0858: if (user.getUserName().equals(INTERNAL_SECURE_USERNAME)) {
0859: return true;
0860: }
0861:
0862: // PENDING: Support column level privileges.
0863:
0864: // Ask the grant manager if there are any privs setup for this user on the
0865: // given schema.
0866: GrantManager manager = context.getGrantManager();
0867: Privileges privs = manager.userGrants(GrantManager.TABLE,
0868: table_name.toString(), user.getUserName());
0869:
0870: return privs.permits(grant);
0871: }
0872:
0873: /**
0874: * Returns true if the user is permitted to create, alter and drop user
0875: * information from the database, otherwise returns false. Only members of
0876: * the 'secure access' group, or the 'user manager' group can do this.
0877: */
0878: public boolean canUserCreateAndDropUsers(
0879: DatabaseQueryContext context, User user)
0880: throws DatabaseException {
0881: return (userHasSecureAccess(context, user) || userBelongsToGroup(
0882: context, user.getUserName(), USER_MANAGER_GROUP));
0883: }
0884:
0885: /**
0886: * Returns true if the user is permitted to create and drop schema's in the
0887: * database, otherwise returns false. Only members of the 'secure access'
0888: * group, or the 'schema manager' group can do this.
0889: */
0890: public boolean canUserCreateAndDropSchema(
0891: DatabaseQueryContext context, User user, String schema)
0892: throws DatabaseException {
0893:
0894: // The internal secure user has full privs on everything
0895: if (user.getUserName().equals(INTERNAL_SECURE_USERNAME)) {
0896: return true;
0897: }
0898:
0899: // No user can create or drop the system schema.
0900: if (schema.equals(SYSTEM_SCHEMA)) {
0901: return false;
0902: } else {
0903: return (userHasSecureAccess(context, user) || userBelongsToGroup(
0904: context, user.getUserName(), SCHEMA_MANAGER_GROUP));
0905: }
0906: }
0907:
0908: /**
0909: * Returns true if the user can shut down the database server. A user can
0910: * shut down the database if they are a member of the 'secure acces' group.
0911: */
0912: public boolean canUserShutDown(DatabaseQueryContext context,
0913: User user) throws DatabaseException {
0914: return userHasSecureAccess(context, user);
0915: }
0916:
0917: /**
0918: * Returns true if the user is allowed to execute the given stored procedure.
0919: */
0920: public boolean canUserExecuteStoredProcedure(
0921: DatabaseQueryContext context, User user,
0922: String procedure_name) throws DatabaseException {
0923: // Currently you can only execute a procedure if you are a member of the
0924: // secure access priv group.
0925: return userHasSecureAccess(context, user);
0926: }
0927:
0928: // ---- General schema level privs ----
0929:
0930: /**
0931: * Returns true if the user can create a table or view with the given name,
0932: * otherwise returns false.
0933: */
0934: public boolean canUserCreateTableObject(
0935: DatabaseQueryContext context, User user, TableName table)
0936: throws DatabaseException {
0937: if (userHasSchemaGrant(context, user, table.getSchema(),
0938: Privileges.CREATE)) {
0939: return true;
0940: }
0941:
0942: // If the user belongs to the secure access priv group, return true
0943: return userHasSecureAccess(context, user);
0944: }
0945:
0946: /**
0947: * Returns true if the user can alter a table or view with the given name,
0948: * otherwise returns false.
0949: */
0950: public boolean canUserAlterTableObject(
0951: DatabaseQueryContext context, User user, TableName table)
0952: throws DatabaseException {
0953: if (userHasSchemaGrant(context, user, table.getSchema(),
0954: Privileges.ALTER)) {
0955: return true;
0956: }
0957:
0958: // If the user belongs to the secure access priv group, return true
0959: return userHasSecureAccess(context, user);
0960: }
0961:
0962: /**
0963: * Returns true if the user can drop a table or view with the given name,
0964: * otherwise returns false.
0965: */
0966: public boolean canUserDropTableObject(DatabaseQueryContext context,
0967: User user, TableName table) throws DatabaseException {
0968: if (userHasSchemaGrant(context, user, table.getSchema(),
0969: Privileges.DROP)) {
0970: return true;
0971: }
0972:
0973: // If the user belongs to the secure access priv group, return true
0974: return userHasSecureAccess(context, user);
0975: }
0976:
0977: // ---- Check table object privs ----
0978:
0979: /**
0980: * Returns true if the user can select from a table or view with the given
0981: * name and given columns, otherwise returns false.
0982: */
0983: public boolean canUserSelectFromTableObject(
0984: DatabaseQueryContext context, User user, TableName table,
0985: Variable[] columns) throws DatabaseException {
0986: if (userHasTableObjectGrant(context, user, table, columns,
0987: Privileges.SELECT)) {
0988: return true;
0989: }
0990:
0991: // If the user belongs to the secure access priv group, return true
0992: return userHasSecureAccess(context, user);
0993: }
0994:
0995: /**
0996: * Returns true if the user can insert into a table or view with the given
0997: * name and given columns, otherwise returns false.
0998: */
0999: public boolean canUserInsertIntoTableObject(
1000: DatabaseQueryContext context, User user, TableName table,
1001: Variable[] columns) throws DatabaseException {
1002: if (userHasTableObjectGrant(context, user, table, columns,
1003: Privileges.INSERT)) {
1004: return true;
1005: }
1006:
1007: // If the user belongs to the secure access priv group, return true
1008: return userHasSecureAccess(context, user);
1009: }
1010:
1011: /**
1012: * Returns true if the user can update a table or view with the given
1013: * name and given columns, otherwise returns false.
1014: */
1015: public boolean canUserUpdateTableObject(
1016: DatabaseQueryContext context, User user, TableName table,
1017: Variable[] columns) throws DatabaseException {
1018: if (userHasTableObjectGrant(context, user, table, columns,
1019: Privileges.UPDATE)) {
1020: return true;
1021: }
1022:
1023: // If the user belongs to the secure access priv group, return true
1024: return userHasSecureAccess(context, user);
1025: }
1026:
1027: /**
1028: * Returns true if the user can delete from a table or view with the given
1029: * name and given columns, otherwise returns false.
1030: */
1031: public boolean canUserDeleteFromTableObject(
1032: DatabaseQueryContext context, User user, TableName table)
1033: throws DatabaseException {
1034: if (userHasTableObjectGrant(context, user, table, null,
1035: Privileges.DELETE)) {
1036: return true;
1037: }
1038:
1039: // If the user belongs to the secure access priv group, return true
1040: return userHasSecureAccess(context, user);
1041: }
1042:
1043: /**
1044: * Returns true if the user can compact a table with the given name,
1045: * otherwise returns false.
1046: */
1047: public boolean canUserCompactTableObject(
1048: DatabaseQueryContext context, User user, TableName table)
1049: throws DatabaseException {
1050: if (userHasTableObjectGrant(context, user, table, null,
1051: Privileges.COMPACT)) {
1052: return true;
1053: }
1054:
1055: // If the user belongs to the secure access priv group, return true
1056: return userHasSecureAccess(context, user);
1057: }
1058:
1059: /**
1060: * Returns true if the user can create a procedure with the given name,
1061: * otherwise returns false.
1062: */
1063: public boolean canUserCreateProcedureObject(
1064: DatabaseQueryContext context, User user, TableName table)
1065: throws DatabaseException {
1066: if (userHasSchemaGrant(context, user, table.getSchema(),
1067: Privileges.CREATE)) {
1068: return true;
1069: }
1070:
1071: // If the user belongs to the secure access priv group, return true
1072: return userHasSecureAccess(context, user);
1073: }
1074:
1075: /**
1076: * Returns true if the user can drop a procedure with the given name,
1077: * otherwise returns false.
1078: */
1079: public boolean canUserDropProcedureObject(
1080: DatabaseQueryContext context, User user, TableName table)
1081: throws DatabaseException {
1082: if (userHasSchemaGrant(context, user, table.getSchema(),
1083: Privileges.DROP)) {
1084: return true;
1085: }
1086:
1087: // If the user belongs to the secure access priv group, return true
1088: return userHasSecureAccess(context, user);
1089: }
1090:
1091: /**
1092: * Returns true if the user can create a sequence with the given name,
1093: * otherwise returns false.
1094: */
1095: public boolean canUserCreateSequenceObject(
1096: DatabaseQueryContext context, User user, TableName table)
1097: throws DatabaseException {
1098: if (userHasSchemaGrant(context, user, table.getSchema(),
1099: Privileges.CREATE)) {
1100: return true;
1101: }
1102:
1103: // If the user belongs to the secure access priv group, return true
1104: return userHasSecureAccess(context, user);
1105: }
1106:
1107: /**
1108: * Returns true if the user can drop a sequence with the given name,
1109: * otherwise returns false.
1110: */
1111: public boolean canUserDropSequenceObject(
1112: DatabaseQueryContext context, User user, TableName table)
1113: throws DatabaseException {
1114: if (userHasSchemaGrant(context, user, table.getSchema(),
1115: Privileges.DROP)) {
1116: return true;
1117: }
1118:
1119: // If the user belongs to the secure access priv group, return true
1120: return userHasSecureAccess(context, user);
1121: }
1122:
1123: // ---------- Schema management ----------
1124:
1125: /**
1126: * Creates the schema information tables introducted in version 0.90. The
1127: * schema information tables are;
1128: */
1129: void createSchemaInfoTables(DatabaseConnection connection)
1130: throws DatabaseException {
1131:
1132: connection.createSchema(DEFAULT_SCHEMA, "DEFAULT");
1133: connection.createSchema(JDBC_SCHEMA, "SYSTEM");
1134:
1135: }
1136:
1137: /**
1138: * Creates all the system views.
1139: */
1140: private void createSystemViews(DatabaseConnection connection)
1141: throws DatabaseException {
1142: // Obtain the JDBC interface.
1143: try {
1144: Connection jdbc = connection.getJDBCConnection();
1145:
1146: // Is the username/password in the database?
1147: Statement stmt = jdbc.createStatement();
1148:
1149: // This view shows the grants that the user has (no join, only priv_bit).
1150: stmt
1151: .executeUpdate("CREATE VIEW SYS_JDBC.ThisUserSimpleGrant AS "
1152: + " SELECT \"priv_bit\", \"object\", \"param\", \"grantee\", "
1153: + " \"grant_option\", \"granter\" "
1154: + " FROM SYS_INFO.sUSRGrant "
1155: + " WHERE ( grantee = user() OR grantee = '@PUBLIC' )");
1156: // This view shows the grants that the user is allowed to see
1157: stmt
1158: .executeUpdate("CREATE VIEW SYS_JDBC.ThisUserGrant AS "
1159: + " SELECT \"description\", \"object\", \"param\", \"grantee\", "
1160: + " \"grant_option\", \"granter\" "
1161: + " FROM SYS_INFO.sUSRGrant, SYS_INFO.sUSRPrivMap "
1162: + " WHERE ( grantee = user() OR grantee = '@PUBLIC' )"
1163: + " AND sUSRGrant.priv_bit = sUSRPrivMap.priv_bit");
1164: // A view that represents the list of schema this user is allowed to view
1165: // the contents of.
1166: stmt
1167: .executeUpdate("CREATE VIEW SYS_JDBC.ThisUserSchemaInfo AS "
1168: + " SELECT * FROM SYS_INFO.sUSRSchemaInfo "
1169: + " WHERE \"name\" IN ( "
1170: + " SELECT \"param\" "
1171: + " FROM SYS_JDBC.ThisUserGrant "
1172: + " WHERE \"object\" = 65 "
1173: + " AND \"description\" = 'LIST' )");
1174: // A view that exposes the sUSRTableColumn table but only for the tables
1175: // this user has read access to.
1176: stmt
1177: .executeUpdate("CREATE VIEW SYS_JDBC.ThisUserTableColumns AS "
1178: + " SELECT * FROM SYS_INFO.sUSRTableColumns "
1179: + " WHERE \"schema\" IN ( "
1180: + " SELECT \"name\" FROM SYS_JDBC.ThisUserSchemaInfo )");
1181: // A view that exposes the sUSRTableInfo table but only for the tables
1182: // this user has read access to.
1183: stmt
1184: .executeUpdate("CREATE VIEW SYS_JDBC.ThisUserTableInfo AS "
1185: + " SELECT * FROM SYS_INFO.sUSRTableInfo "
1186: + " WHERE \"schema\" IN ( "
1187: + " SELECT \"name\" FROM SYS_JDBC.ThisUserSchemaInfo )");
1188:
1189: // A JDBC helper view for the 'getTables' meta-data method
1190: stmt
1191: .executeUpdate(" CREATE VIEW SYS_JDBC.Tables AS "
1192: + " SELECT NULL AS \"TABLE_CAT\", \n"
1193: + " \"schema\" AS \"TABLE_SCHEM\", \n"
1194: + " \"name\" AS \"TABLE_NAME\", \n"
1195: + " \"type\" AS \"TABLE_TYPE\", \n"
1196: + " \"other\" AS \"REMARKS\", \n"
1197: + " NULL AS \"TYPE_CAT\", \n"
1198: + " NULL AS \"TYPE_SCHEM\", \n"
1199: + " NULL AS \"TYPE_NAME\", \n"
1200: + " NULL AS \"SELF_REFERENCING_COL_NAME\", \n"
1201: + " NULL AS \"REF_GENERATION\" \n"
1202: + " FROM SYS_JDBC.ThisUserTableInfo \n");
1203: // A JDBC helper view for the 'getSchemas' meta-data method
1204: stmt.executeUpdate(" CREATE VIEW SYS_JDBC.Schemas AS "
1205: + " SELECT \"name\" AS \"TABLE_SCHEM\", \n"
1206: + " NULL AS \"TABLE_CATALOG\" \n"
1207: + " FROM SYS_JDBC.ThisUserSchemaInfo\n");
1208: // A JDBC helper view for the 'getCatalogs' meta-data method
1209: stmt.executeUpdate(" CREATE VIEW SYS_JDBC.Catalogs AS "
1210: + " SELECT NULL AS \"TABLE_CAT\" \n"
1211: + " FROM SYS_INFO.sUSRSchemaInfo\n" + // Hacky, this will generate a 0 row
1212: " WHERE FALSE\n"); // table.
1213: // A JDBC helper view for the 'getColumns' meta-data method
1214: stmt
1215: .executeUpdate(" CREATE VIEW SYS_JDBC.Columns AS "
1216: + " SELECT NULL AS \"TABLE_CAT\",\n"
1217: + " \"schema\" AS \"TABLE_SCHEM\",\n"
1218: + " \"table\" AS \"TABLE_NAME\",\n"
1219: + " \"column\" AS \"COLUMN_NAME\",\n"
1220: + " \"sql_type\" AS \"DATA_TYPE\",\n"
1221: + " \"type_desc\" AS \"TYPE_NAME\",\n"
1222: + " IF(\"size\" = -1, 1024, \"size\") AS \"COLUMN_SIZE\",\n"
1223: + " NULL AS \"BUFFER_LENGTH\",\n"
1224: + " \"scale\" AS \"DECIMAL_DIGITS\",\n"
1225: + " IF(\"sql_type\" = -7, 2, 10) AS \"NUM_PREC_RADIX\",\n"
1226: + " IF(\"not_null\", 0, 1) AS \"NULLABLE\",\n"
1227: + " '' AS \"REMARKS\",\n"
1228: + " \"default\" AS \"COLUMN_DEF\",\n"
1229: + " NULL AS \"SQL_DATA_TYPE\",\n"
1230: + " NULL AS \"SQL_DATETIME_SUB\",\n"
1231: + " IF(\"size\" = -1, 1024, \"size\") AS \"CHAR_OCTET_LENGTH\",\n"
1232: + " \"seq_no\" + 1 AS \"ORDINAL_POSITION\",\n"
1233: + " IF(\"not_null\", 'NO', 'YES') AS \"IS_NULLABLE\"\n"
1234: + " FROM SYS_JDBC.ThisUserTableColumns\n");
1235: // A JDBC helper view for the 'getColumnPrivileges' meta-data method
1236: stmt
1237: .executeUpdate(" CREATE VIEW SYS_JDBC.ColumnPrivileges AS "
1238: + " SELECT \"TABLE_CAT\",\n"
1239: + " \"TABLE_SCHEM\",\n"
1240: + " \"TABLE_NAME\",\n"
1241: + " \"COLUMN_NAME\",\n"
1242: + " IF(\"ThisUserGrant.granter\" = '@SYSTEM', \n"
1243: + " NULL, \"ThisUserGrant.granter\") AS \"GRANTOR\",\n"
1244: + " IF(\"ThisUserGrant.grantee\" = '@PUBLIC', \n"
1245: + " 'public', \"ThisUserGrant.grantee\") AS \"GRANTEE\",\n"
1246: + " \"ThisUserGrant.description\" AS \"PRIVILEGE\",\n"
1247: + " IF(\"grant_option\" = 'true', 'YES', 'NO') AS \"IS_GRANTABLE\" \n"
1248: + " FROM SYS_JDBC.Columns, SYS_JDBC.ThisUserGrant \n"
1249: + " WHERE CONCAT(Columns.TABLE_SCHEM, '.', Columns.TABLE_NAME) = \n"
1250: + " ThisUserGrant.param \n"
1251: + " AND SYS_JDBC.ThisUserGrant.object = 1 \n"
1252: + " AND SYS_JDBC.ThisUserGrant.description IS NOT NULL \n");
1253: // A JDBC helper view for the 'getTablePrivileges' meta-data method
1254: stmt
1255: .executeUpdate(" CREATE VIEW SYS_JDBC.TablePrivileges AS "
1256: + " SELECT \"TABLE_CAT\",\n"
1257: + " \"TABLE_SCHEM\",\n"
1258: + " \"TABLE_NAME\",\n"
1259: + " IF(\"ThisUserGrant.granter\" = '@SYSTEM', \n"
1260: + " NULL, \"ThisUserGrant.granter\") AS \"GRANTOR\",\n"
1261: + " IF(\"ThisUserGrant.grantee\" = '@PUBLIC', \n"
1262: + " 'public', \"ThisUserGrant.grantee\") AS \"GRANTEE\",\n"
1263: + " \"ThisUserGrant.description\" AS \"PRIVILEGE\",\n"
1264: + " IF(\"grant_option\" = 'true', 'YES', 'NO') AS \"IS_GRANTABLE\" \n"
1265: + " FROM SYS_JDBC.Tables, SYS_JDBC.ThisUserGrant \n"
1266: + " WHERE CONCAT(Tables.TABLE_SCHEM, '.', Tables.TABLE_NAME) = \n"
1267: + " ThisUserGrant.param \n"
1268: + " AND SYS_JDBC.ThisUserGrant.object = 1 \n"
1269: + " AND SYS_JDBC.ThisUserGrant.description IS NOT NULL \n");
1270: // A JDBC helper view for the 'getPrimaryKeys' meta-data method
1271: stmt
1272: .executeUpdate(" CREATE VIEW SYS_JDBC.PrimaryKeys AS "
1273: + " SELECT NULL \"TABLE_CAT\",\n"
1274: + " \"schema\" \"TABLE_SCHEM\",\n"
1275: + " \"table\" \"TABLE_NAME\",\n"
1276: + " \"column\" \"COLUMN_NAME\",\n"
1277: + " \"SYS_INFO.sUSRPrimaryColumns.seq_no\" \"KEY_SEQ\",\n"
1278: + " \"name\" \"PK_NAME\"\n"
1279: + " FROM SYS_INFO.sUSRPKeyInfo, SYS_INFO.sUSRPrimaryColumns\n"
1280: + " WHERE sUSRPKeyInfo.id = sUSRPrimaryColumns.pk_id\n"
1281: + " AND \"schema\" IN\n"
1282: + " ( SELECT \"name\" FROM SYS_JDBC.ThisUserSchemaInfo )\n");
1283: // A JDBC helper view for the 'getImportedKeys' meta-data method
1284: stmt
1285: .executeUpdate(" CREATE VIEW SYS_JDBC.ImportedKeys AS "
1286: + " SELECT NULL \"PKTABLE_CAT\",\n"
1287: + " \"sUSRFKeyInfo.ref_schema\" \"PKTABLE_SCHEM\",\n"
1288: + " \"sUSRFKeyInfo.ref_table\" \"PKTABLE_NAME\",\n"
1289: + " \"sUSRForeignColumns.pcolumn\" \"PKCOLUMN_NAME\",\n"
1290: + " NULL \"FKTABLE_CAT\",\n"
1291: + " \"sUSRFKeyInfo.schema\" \"FKTABLE_SCHEM\",\n"
1292: + " \"sUSRFKeyInfo.table\" \"FKTABLE_NAME\",\n"
1293: + " \"sUSRForeignColumns.fcolumn\" \"FKCOLUMN_NAME\",\n"
1294: + " \"sUSRForeignColumns.seq_no\" \"KEY_SEQ\",\n"
1295: + " I_FRULE_CONVERT(\"sUSRFKeyInfo.update_rule\") \"UPDATE_RULE\",\n"
1296: + " I_FRULE_CONVERT(\"sUSRFKeyInfo.delete_rule\") \"DELETE_RULE\",\n"
1297: + " \"sUSRFKeyInfo.name\" \"FK_NAME\",\n"
1298: + " NULL \"PK_NAME\",\n"
1299: + " \"sUSRFKeyInfo.deferred\" \"DEFERRABILITY\"\n"
1300: + " FROM SYS_INFO.sUSRFKeyInfo, SYS_INFO.sUSRForeignColumns\n"
1301: + " WHERE sUSRFKeyInfo.id = sUSRForeignColumns.fk_id\n"
1302: + " AND \"sUSRFKeyInfo.schema\" IN\n"
1303: + " ( SELECT \"name\" FROM SYS_JDBC.ThisUserSchemaInfo )\n");
1304: // A JDBC helper view for the 'getExportedKeys' meta-data method
1305: stmt
1306: .executeUpdate(" CREATE VIEW SYS_JDBC.ExportedKeys AS "
1307: + " SELECT NULL \"PKTABLE_CAT\",\n"
1308: + " \"sUSRFKeyInfo.ref_schema\" \"PKTABLE_SCHEM\",\n"
1309: + " \"sUSRFKeyInfo.ref_table\" \"PKTABLE_NAME\",\n"
1310: + " \"sUSRForeignColumns.pcolumn\" \"PKCOLUMN_NAME\",\n"
1311: + " NULL \"FKTABLE_CAT\",\n"
1312: + " \"sUSRFKeyInfo.schema\" \"FKTABLE_SCHEM\",\n"
1313: + " \"sUSRFKeyInfo.table\" \"FKTABLE_NAME\",\n"
1314: + " \"sUSRForeignColumns.fcolumn\" \"FKCOLUMN_NAME\",\n"
1315: + " \"sUSRForeignColumns.seq_no\" \"KEY_SEQ\",\n"
1316: + " I_FRULE_CONVERT(\"sUSRFKeyInfo.update_rule\") \"UPDATE_RULE\",\n"
1317: + " I_FRULE_CONVERT(\"sUSRFKeyInfo.delete_rule\") \"DELETE_RULE\",\n"
1318: + " \"sUSRFKeyInfo.name\" \"FK_NAME\",\n"
1319: + " NULL \"PK_NAME\",\n"
1320: + " \"sUSRFKeyInfo.deferred\" \"DEFERRABILITY\"\n"
1321: + " FROM SYS_INFO.sUSRFKeyInfo, SYS_INFO.sUSRForeignColumns\n"
1322: + " WHERE sUSRFKeyInfo.id = sUSRForeignColumns.fk_id\n"
1323: + " AND \"sUSRFKeyInfo.schema\" IN\n"
1324: + " ( SELECT \"name\" FROM SYS_JDBC.ThisUserSchemaInfo )\n");
1325: // A JDBC helper view for the 'getCrossReference' meta-data method
1326: stmt
1327: .executeUpdate(" CREATE VIEW SYS_JDBC.CrossReference AS "
1328: + " SELECT NULL \"PKTABLE_CAT\",\n"
1329: + " \"sUSRFKeyInfo.ref_schema\" \"PKTABLE_SCHEM\",\n"
1330: + " \"sUSRFKeyInfo.ref_table\" \"PKTABLE_NAME\",\n"
1331: + " \"sUSRForeignColumns.pcolumn\" \"PKCOLUMN_NAME\",\n"
1332: + " NULL \"FKTABLE_CAT\",\n"
1333: + " \"sUSRFKeyInfo.schema\" \"FKTABLE_SCHEM\",\n"
1334: + " \"sUSRFKeyInfo.table\" \"FKTABLE_NAME\",\n"
1335: + " \"sUSRForeignColumns.fcolumn\" \"FKCOLUMN_NAME\",\n"
1336: + " \"sUSRForeignColumns.seq_no\" \"KEY_SEQ\",\n"
1337: + " I_FRULE_CONVERT(\"sUSRFKeyInfo.update_rule\") \"UPDATE_RULE\",\n"
1338: + " I_FRULE_CONVERT(\"sUSRFKeyInfo.delete_rule\") \"DELETE_RULE\",\n"
1339: + " \"sUSRFKeyInfo.name\" \"FK_NAME\",\n"
1340: + " NULL \"PK_NAME\",\n"
1341: + " \"sUSRFKeyInfo.deferred\" \"DEFERRABILITY\"\n"
1342: + " FROM SYS_INFO.sUSRFKeyInfo, SYS_INFO.sUSRForeignColumns\n"
1343: + " WHERE sUSRFKeyInfo.id = sUSRForeignColumns.fk_id\n"
1344: + " AND \"sUSRFKeyInfo.schema\" IN\n"
1345: + " ( SELECT \"name\" FROM SYS_JDBC.ThisUserSchemaInfo )\n");
1346:
1347: } catch (SQLException e) {
1348: if (e instanceof MSQLException) {
1349: MSQLException msqle = (MSQLException) e;
1350: Debug().write(Lvl.ERROR, this ,
1351: msqle.getServerErrorStackTrace());
1352: }
1353: Debug().writeException(Lvl.ERROR, e);
1354: throw new RuntimeException("SQL Error: " + e.getMessage());
1355: }
1356:
1357: }
1358:
1359: /**
1360: * Creates all the priv/password system tables.
1361: */
1362: private void createSystemTables(DatabaseConnection connection)
1363: throws DatabaseException {
1364:
1365: // --- The user management tables ---
1366: DataTableDef sUSRPassword = new DataTableDef();
1367: sUSRPassword.setTableName(SYS_PASSWORD);
1368: sUSRPassword.addColumn(DataTableColumnDef
1369: .createStringColumn("UserName"));
1370: sUSRPassword.addColumn(DataTableColumnDef
1371: .createStringColumn("Password"));
1372:
1373: DataTableDef sUSRUserPriv = new DataTableDef();
1374: sUSRUserPriv.setTableName(SYS_USERPRIV);
1375: sUSRUserPriv.addColumn(DataTableColumnDef
1376: .createStringColumn("UserName"));
1377: sUSRUserPriv.addColumn(DataTableColumnDef
1378: .createStringColumn("PrivGroupName"));
1379:
1380: DataTableDef sUSRUserConnectPriv = new DataTableDef();
1381: sUSRUserConnectPriv.setTableName(SYS_USERCONNECT);
1382: sUSRUserConnectPriv.addColumn(DataTableColumnDef
1383: .createStringColumn("UserName"));
1384: sUSRUserConnectPriv.addColumn(DataTableColumnDef
1385: .createStringColumn("Protocol"));
1386: sUSRUserConnectPriv.addColumn(DataTableColumnDef
1387: .createStringColumn("Host"));
1388: sUSRUserConnectPriv.addColumn(DataTableColumnDef
1389: .createStringColumn("Access"));
1390:
1391: DataTableDef sUSRGrant = new DataTableDef();
1392: sUSRGrant.setTableName(SYS_GRANTS);
1393: sUSRGrant.addColumn(DataTableColumnDef
1394: .createNumericColumn("priv_bit"));
1395: sUSRGrant.addColumn(DataTableColumnDef
1396: .createNumericColumn("object"));
1397: sUSRGrant.addColumn(DataTableColumnDef
1398: .createStringColumn("param"));
1399: sUSRGrant.addColumn(DataTableColumnDef
1400: .createStringColumn("grantee"));
1401: sUSRGrant.addColumn(DataTableColumnDef
1402: .createStringColumn("grant_option"));
1403: sUSRGrant.addColumn(DataTableColumnDef
1404: .createStringColumn("granter"));
1405:
1406: DataTableDef sUSRService = new DataTableDef();
1407: sUSRService.setTableName(SYS_SERVICE);
1408: sUSRService.addColumn(DataTableColumnDef
1409: .createStringColumn("name"));
1410: sUSRService.addColumn(DataTableColumnDef
1411: .createStringColumn("class"));
1412: sUSRService.addColumn(DataTableColumnDef
1413: .createStringColumn("type"));
1414:
1415: DataTableDef sUSRFunctionFactory = new DataTableDef();
1416: sUSRFunctionFactory.setTableName(SYS_FUNCTIONFACTORY);
1417: sUSRFunctionFactory.addColumn(DataTableColumnDef
1418: .createStringColumn("name"));
1419: sUSRFunctionFactory.addColumn(DataTableColumnDef
1420: .createStringColumn("class"));
1421: sUSRFunctionFactory.addColumn(DataTableColumnDef
1422: .createStringColumn("type"));
1423:
1424: DataTableDef sUSRFunction = new DataTableDef();
1425: sUSRFunction.setTableName(SYS_FUNCTION);
1426: sUSRFunction.addColumn(DataTableColumnDef
1427: .createStringColumn("schema"));
1428: sUSRFunction.addColumn(DataTableColumnDef
1429: .createStringColumn("name"));
1430: sUSRFunction.addColumn(DataTableColumnDef
1431: .createStringColumn("type"));
1432: sUSRFunction.addColumn(DataTableColumnDef
1433: .createStringColumn("location"));
1434: sUSRFunction.addColumn(DataTableColumnDef
1435: .createStringColumn("return_type"));
1436: sUSRFunction.addColumn(DataTableColumnDef
1437: .createStringColumn("args_type"));
1438: sUSRFunction.addColumn(DataTableColumnDef
1439: .createStringColumn("username"));
1440:
1441: DataTableDef sUSRView = new DataTableDef();
1442: sUSRView.setTableName(SYS_VIEW);
1443: sUSRView.addColumn(DataTableColumnDef
1444: .createStringColumn("schema"));
1445: sUSRView.addColumn(DataTableColumnDef
1446: .createStringColumn("name"));
1447: sUSRView.addColumn(DataTableColumnDef
1448: .createBinaryColumn("query"));
1449: sUSRView.addColumn(DataTableColumnDef
1450: .createBinaryColumn("data"));
1451: sUSRView.addColumn(DataTableColumnDef
1452: .createStringColumn("username"));
1453:
1454: DataTableDef sUSRLabel = new DataTableDef();
1455: sUSRLabel.setTableName(SYS_LABEL);
1456: sUSRLabel.addColumn(DataTableColumnDef
1457: .createNumericColumn("object_type"));
1458: sUSRLabel.addColumn(DataTableColumnDef
1459: .createStringColumn("object_name"));
1460: sUSRLabel.addColumn(DataTableColumnDef
1461: .createStringColumn("label"));
1462:
1463: DataTableDef sUSRDataTrigger = new DataTableDef();
1464: sUSRDataTrigger.setTableName(SYS_DATA_TRIGGER);
1465: sUSRDataTrigger.addColumn(DataTableColumnDef
1466: .createStringColumn("schema"));
1467: sUSRDataTrigger.addColumn(DataTableColumnDef
1468: .createStringColumn("name"));
1469: sUSRDataTrigger.addColumn(DataTableColumnDef
1470: .createNumericColumn("type"));
1471: sUSRDataTrigger.addColumn(DataTableColumnDef
1472: .createStringColumn("on_object"));
1473: sUSRDataTrigger.addColumn(DataTableColumnDef
1474: .createStringColumn("action"));
1475: sUSRDataTrigger.addColumn(DataTableColumnDef
1476: .createBinaryColumn("misc"));
1477: sUSRDataTrigger.addColumn(DataTableColumnDef
1478: .createStringColumn("username"));
1479:
1480: // Create the tables
1481: connection.alterCreateTable(sUSRPassword, 91, 128);
1482: connection.alterCreateTable(sUSRUserPriv, 91, 128);
1483: connection.alterCreateTable(sUSRUserConnectPriv, 91, 128);
1484: connection.alterCreateTable(sUSRGrant, 195, 128);
1485: connection.alterCreateTable(sUSRService, 91, 128);
1486: connection.alterCreateTable(sUSRFunctionFactory, 91, 128);
1487: connection.alterCreateTable(sUSRFunction, 91, 128);
1488: connection.alterCreateTable(sUSRView, 91, 128);
1489: connection.alterCreateTable(sUSRLabel, 91, 128);
1490: connection.alterCreateTable(sUSRDataTrigger, 91, 128);
1491:
1492: }
1493:
1494: /**
1495: * Sets all the standard functions and procedures available to engine.
1496: * This creates an entry in the SYS_FUNCTION table for all the dynamic
1497: * functions and procedures. This may not include the functions exposed
1498: * though the FunctionFactory interface.
1499: */
1500: public void setupSystemFunctions(DatabaseConnection connection,
1501: String admin_user) throws DatabaseException {
1502:
1503: final String GRANTER = INTERNAL_SECURE_USERNAME;
1504:
1505: // The manager handling the functions.
1506: ProcedureManager manager = connection.getProcedureManager();
1507:
1508: // Define the SYSTEM_MAKE_BACKUP procedure
1509: Class c = com.mckoi.database.procedure.SystemBackup.class;
1510: manager
1511: .defineJavaProcedure(
1512: new ProcedureName(SYSTEM_SCHEMA,
1513: "SYSTEM_MAKE_BACKUP"),
1514: "com.mckoi.database.procedure.SystemBackup.invoke(ProcedureConnection, String)",
1515: TType.STRING_TYPE,
1516: new TType[] { TType.STRING_TYPE }, admin_user);
1517:
1518: // -----
1519:
1520: // Set the grants for the procedures.
1521: GrantManager grants = connection.getGrantManager();
1522:
1523: // Revoke all existing grants on the internal stored procedures.
1524: grants.revokeAllGrantsOnObject(GrantManager.TABLE,
1525: "SYS_INFO.SYSTEM_MAKE_BACKUP");
1526:
1527: // Grant execute priv with grant option to administrator
1528: grants.addGrant(Privileges.PROCEDURE_EXECUTE_PRIVS,
1529: GrantManager.TABLE, "SYS_INFO.SYSTEM_MAKE_BACKUP",
1530: admin_user, true, GRANTER);
1531:
1532: }
1533:
1534: /**
1535: * Clears all the grant information in the sUSRGrant table. This should only
1536: * be used if we need to refresh the grant information for whatever reason
1537: * (such as when converting between different versions).
1538: */
1539: private void clearAllGrants(DatabaseConnection connection)
1540: throws DatabaseException {
1541: DataTable grant_table = connection.getTable(SYS_GRANTS);
1542: grant_table.delete(grant_table);
1543: }
1544:
1545: /**
1546: * Set up the system table grants.
1547: * <p>
1548: * This gives the grantee user full access to sUSRPassword,
1549: * sUSRUserPriv, sUSRUserConnectPriv, sUSRService, sUSRFunctionFactory,
1550: * and sUSRFunction. All other sUSR tables are granted SELECT only.
1551: * If 'grant_option' is true then the user is given the option to give the
1552: * grants to other users.
1553: */
1554: private void setSystemGrants(DatabaseConnection connection,
1555: String grantee) throws DatabaseException {
1556:
1557: final String GRANTER = INTERNAL_SECURE_USERNAME;
1558:
1559: // Add all priv grants to those that the system user is allowed to change
1560: GrantManager manager = connection.getGrantManager();
1561:
1562: // Add schema grant for APP
1563: manager.addGrant(Privileges.SCHEMA_ALL_PRIVS,
1564: GrantManager.SCHEMA, "APP", grantee, true, GRANTER);
1565: // Add public grant for SYS_INFO
1566: manager.addGrant(Privileges.SCHEMA_READ_PRIVS,
1567: GrantManager.SCHEMA, "SYS_INFO",
1568: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1569: // Add public grant for SYS_JDBC
1570: manager.addGrant(Privileges.SCHEMA_READ_PRIVS,
1571: GrantManager.SCHEMA, "SYS_JDBC",
1572: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1573:
1574: // For all tables in the SYS_INFO schema, grant all privileges to the
1575: // system user.
1576: manager.addGrantToAllTablesInSchema("SYS_INFO",
1577: Privileges.TABLE_ALL_PRIVS, grantee, false, GRANTER);
1578:
1579: // Set the public grants for the system tables,
1580: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1581: GrantManager.TABLE, "SYS_INFO.sUSRConnectionInfo",
1582: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1583: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1584: GrantManager.TABLE, "SYS_INFO.sUSRCurrentConnections",
1585: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1586: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1587: GrantManager.TABLE, "SYS_INFO.sUSRDatabaseStatistics",
1588: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1589: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1590: GrantManager.TABLE, "SYS_INFO.sUSRDatabaseVars",
1591: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1592: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1593: GrantManager.TABLE, "SYS_INFO.sUSRProductInfo",
1594: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1595: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1596: GrantManager.TABLE, "SYS_INFO.sUSRSQLTypeInfo",
1597: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1598:
1599: // Set public grants for the system views.
1600: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1601: GrantManager.TABLE, "SYS_JDBC.ThisUserGrant",
1602: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1603: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1604: GrantManager.TABLE, "SYS_JDBC.ThisUserSimpleGrant",
1605: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1606: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1607: GrantManager.TABLE, "SYS_JDBC.ThisUserSchemaInfo",
1608: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1609: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1610: GrantManager.TABLE, "SYS_JDBC.ThisUserTableColumns",
1611: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1612: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1613: GrantManager.TABLE, "SYS_JDBC.ThisUserTableInfo",
1614: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1615:
1616: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1617: GrantManager.TABLE, "SYS_JDBC.Tables",
1618: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1619: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1620: GrantManager.TABLE, "SYS_JDBC.Schemas",
1621: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1622: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1623: GrantManager.TABLE, "SYS_JDBC.Catalogs",
1624: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1625: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1626: GrantManager.TABLE, "SYS_JDBC.Columns",
1627: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1628: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1629: GrantManager.TABLE, "SYS_JDBC.ColumnPrivileges",
1630: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1631: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1632: GrantManager.TABLE, "SYS_JDBC.TablePrivileges",
1633: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1634: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1635: GrantManager.TABLE, "SYS_JDBC.PrimaryKeys",
1636: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1637: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1638: GrantManager.TABLE, "SYS_JDBC.ImportedKeys",
1639: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1640: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1641: GrantManager.TABLE, "SYS_JDBC.ExportedKeys",
1642: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1643: manager.addGrant(Privileges.TABLE_READ_PRIVS,
1644: GrantManager.TABLE, "SYS_JDBC.CrossReference",
1645: GrantManager.PUBLIC_USERNAME_STR, false, GRANTER);
1646:
1647: }
1648:
1649: /**
1650: * Sets the system table listeners on the SYS_INFO.sUSRView table. These
1651: * listeners are used to cache information
1652: * that is stored and retrieved from those tables.
1653: */
1654: private void setSystemTableListeners() {
1655: // getSystem().addMasterTableListener(SYS_VIEW, new ViewTableListener());
1656: }
1657:
1658: /**
1659: * Goes through all tables in the database not in the SYS_INFO schema and
1660: * adds an entry in the grant table for it.
1661: * <p>
1662: * This is for converting from a pre-grant database.
1663: *
1664: * @param connection the database transaction
1665: * @param grantee the grantee to apply the table privs to
1666: */
1667: private void convertPreGrant(DatabaseConnection connection,
1668: String grantee) throws DatabaseException {
1669:
1670: String GRANTER = INTERNAL_SECURE_USERNAME;
1671: GrantManager manager = connection.getGrantManager();
1672:
1673: // Setup grants for any user schema that have been created.
1674: SchemaDef[] all_schema = connection.getSchemaList();
1675: for (int i = 0; i < all_schema.length; ++i) {
1676: SchemaDef schema = all_schema[i];
1677: // The admin user is given full privs to all tables in USER or DEFAULT
1678: // schema.
1679: if (schema.getType().equals("USER")
1680: || schema.getType().equals("DEFAULT")) {
1681: // Don't set grants for default schema
1682: if (!schema.getType().equals("DEFAULT")) {
1683: manager.addGrant(Privileges.TABLE_ALL_PRIVS,
1684: GrantManager.SCHEMA, schema.getName(),
1685: grantee, true, GRANTER);
1686: }
1687: manager.addGrantToAllTablesInSchema(schema.getName(),
1688: Privileges.TABLE_ALL_PRIVS, grantee, true,
1689: GRANTER);
1690: }
1691: }
1692:
1693: }
1694:
1695: /**
1696: * Converts tables from a database that are pre database schema.
1697: */
1698: private void convertPreSchema(DatabaseConnection connection)
1699: throws DatabaseException {
1700: throw new DatabaseException(
1701: "Converting from pre-schema no longer supported.");
1702: }
1703:
1704: /**
1705: * Creates and sets up a new database to an initial empty state. The
1706: * creation process involves creating all the system tables and views, adding
1707: * an administrator user account, creating schema, and setting up the initial
1708: * grant information for the administrator user.
1709: * <p>
1710: * The 'username' and 'password' parameter given are set for the administrator
1711: * account.
1712: */
1713: public void create(String username, String password) {
1714:
1715: if (isReadOnly()) {
1716: throw new RuntimeException(
1717: "Can not create database in read only mode.");
1718: }
1719:
1720: if (username == null || username.length() == 0
1721: || password == null || password.length() == 0) {
1722: throw new RuntimeException(
1723: "Must have valid username and password String");
1724: }
1725:
1726: try {
1727: // Create the conglomerate
1728: conglomerate.create(getName());
1729:
1730: DatabaseConnection connection = createNewConnection(null,
1731: null);
1732: DatabaseQueryContext context = new DatabaseQueryContext(
1733: connection);
1734: connection.getLockingMechanism().setMode(
1735: LockingMechanism.EXCLUSIVE_MODE);
1736: connection.setCurrentSchema(SYSTEM_SCHEMA);
1737:
1738: // Create the schema information tables introduced in version 0.90
1739: // and 0.94
1740: createSchemaInfoTables(connection);
1741:
1742: // The system tables that are present in every conglomerate.
1743: createSystemTables(connection);
1744: // Create the system views
1745: createSystemViews(connection);
1746:
1747: // Creates the administrator user.
1748: createUser(context, username, password);
1749: // This is the admin user so add to the 'secure access' table.
1750: addUserToGroup(context, username, SECURE_GROUP);
1751: // Allow all localhost TCP connections.
1752: // NOTE: Permissive initial security!
1753: grantHostAccessToUser(context, username, "TCP", "%");
1754: // Allow all Local connections (from within JVM).
1755: grantHostAccessToUser(context, username, "Local", "%");
1756:
1757: // Sets the system grants for the administrator
1758: setSystemGrants(connection, username);
1759:
1760: // Set all default system procedures.
1761: setupSystemFunctions(connection, username);
1762:
1763: try {
1764: // Close and commit this transaction.
1765: connection.commit();
1766: } catch (TransactionException e) {
1767: Debug().writeException(e);
1768: throw new Error("Transaction Error: " + e.getMessage());
1769: }
1770:
1771: connection.getLockingMechanism().finishMode(
1772: LockingMechanism.EXCLUSIVE_MODE);
1773: connection.close();
1774:
1775: // Close the conglomerate.
1776: conglomerate.close();
1777:
1778: } catch (DatabaseException e) {
1779: Debug().writeException(e);
1780: throw new Error("Database Exception: " + e.getMessage());
1781: } catch (IOException e) {
1782: Debug().writeException(e);
1783: throw new Error("IO Error: " + e.getMessage());
1784: }
1785:
1786: }
1787:
1788: /**
1789: * Initializes the database. This opens all the files that are required for
1790: * the operation of the database. If it finds that the version of the
1791: * data files are not a compatible version, this method throws an exception.
1792: * <p>
1793: * NOTE: Perhaps a better name for this method is 'open'.
1794: */
1795: public void init() throws DatabaseException {
1796:
1797: if (initialised) {
1798: throw new RuntimeException(
1799: "Init() method can only be called once.");
1800: }
1801:
1802: // Reset all session statistics.
1803: stats().resetSession();
1804:
1805: try {
1806: File log_path = system.getLogDirectory();
1807: if (log_path != null && system.logQueries()) {
1808: commands_log = new Log(new File(log_path.getPath(),
1809: "commands.log"), 256 * 1024, 5);
1810: } else {
1811: commands_log = Log.nullLog();
1812: }
1813:
1814: // Check if the state file exists. If it doesn't, we need to report
1815: // incorrect version.
1816: if (!storeSystem().storeExists(getName() + "_sf")) {
1817: // If state store doesn't exist but the legacy style '.sf' state file
1818: // exists,
1819: if (system.getDatabasePath() != null
1820: && new File(system.getDatabasePath(), getName()
1821: + ".sf").exists()) {
1822: throw new DatabaseException(
1823: "The state store for this database doesn't exist. This means "
1824: + "the database version is pre version 1.0. Please see the "
1825: + "README for the details for converting this database.");
1826: } else {
1827: // If neither store or state file exist, assume database doesn't
1828: // exist.
1829: throw new DatabaseException(
1830: "The database does not exist.");
1831: }
1832: }
1833:
1834: // Open the conglomerate
1835: conglomerate.open(getName());
1836:
1837: // Check the state of the conglomerate,
1838: DatabaseConnection connection = createNewConnection(null,
1839: null);
1840: DatabaseQueryContext context = new DatabaseQueryContext(
1841: connection);
1842: connection.getLockingMechanism().setMode(
1843: LockingMechanism.EXCLUSIVE_MODE);
1844: if (!connection
1845: .tableExists(TableDataConglomerate.PERSISTENT_VAR_TABLE)) {
1846: throw new DatabaseException(
1847: "The sUSRDatabaseVars table doesn't exist. This means the "
1848: + "database is pre-schema version 1 or the table has been deleted."
1849: + "If you are converting an old version of the database, please "
1850: + "convert the database using an older release.");
1851: }
1852:
1853: // What version is the data?
1854: DataTable database_vars = connection
1855: .getTable(TableDataConglomerate.PERSISTENT_VAR_TABLE);
1856: Map vars = database_vars.toMap();
1857: String db_version = vars.get("database.version").toString();
1858: // If the version doesn't equal the current version, throw an error.
1859: if (!db_version.equals("1.4")) {
1860: throw new DatabaseException(
1861: "Incorrect data file version '"
1862: + db_version
1863: + "'. Please see "
1864: + "the README on how to convert the data files to the current "
1865: + "version.");
1866: }
1867:
1868: // Commit and close the connection.
1869: connection.commit();
1870: connection.getLockingMechanism().finishMode(
1871: LockingMechanism.EXCLUSIVE_MODE);
1872: connection.close();
1873:
1874: } catch (TransactionException e) {
1875: // This would be very strange error to receive for in initializing
1876: // database...
1877: throw new Error("Transaction Error: " + e.getMessage());
1878: } catch (IOException e) {
1879: e.printStackTrace(System.err);
1880: throw new Error("IO Error: " + e.getMessage());
1881: }
1882:
1883: // Sets up the system table listeners
1884: setSystemTableListeners();
1885:
1886: initialised = true;
1887:
1888: }
1889:
1890: /**
1891: * Cleanly shuts down the database. It is important that this method is
1892: * called just before the system closes down.
1893: * <p>
1894: * The main purpose of this method is to ensure any tables that are backed
1895: * by files and in a 'safe' state and cleanly flushed to the file system.
1896: * <p>
1897: * If 'delete_on_shutdown' is true, the database will delete itself from the
1898: * file system when it shuts down.
1899: */
1900: public void shutdown() throws DatabaseException {
1901:
1902: if (initialised == false) {
1903: throw new Error("The database is not initialized.");
1904: }
1905:
1906: try {
1907: if (delete_on_shutdown == true) {
1908: // Delete the conglomerate if the database is set to delete on
1909: // shutdown.
1910: conglomerate.delete();
1911: } else {
1912: // Otherwise close the conglomerate.
1913: conglomerate.close();
1914: }
1915: } catch (IOException e) {
1916: Debug().writeException(e);
1917: throw new Error("IO Error: " + e.getMessage());
1918: }
1919:
1920: // Shut down the logs...
1921: if (commands_log != null) {
1922: commands_log.close();
1923: }
1924:
1925: initialised = false;
1926:
1927: }
1928:
1929: /**
1930: * Returns true if the database exists. This must be called before 'init'
1931: * and 'create'. It checks that the database files exist and we can boot
1932: * into the database.
1933: */
1934: public boolean exists() {
1935: if (initialised == true) {
1936: throw new RuntimeException(
1937: "The database is initialised, so no point testing it's existance.");
1938: }
1939:
1940: try {
1941: // HACK: If the legacy style '.sf' state file exists then we must return
1942: // true here because technically the database exists but is not in the
1943: // correct version.
1944: if (conglomerate.exists(getName())) {
1945: return true;
1946: } else {
1947: boolean is_file_s_system = (system.storeSystem() instanceof V1FileStoreSystem);
1948: if (is_file_s_system
1949: && new File(system.getDatabasePath(), getName()
1950: + ".sf").exists()) {
1951: return true;
1952: }
1953: }
1954: return false;
1955: } catch (IOException e) {
1956: Debug().writeException(e);
1957: throw new RuntimeException("IO Error: " + e.getMessage());
1958: }
1959:
1960: }
1961:
1962: /**
1963: * If the 'deleteOnShutdown' flag is set, the database will delete the
1964: * database from the file system when it is shutdown.
1965: * <p>
1966: * NOTE: Use with care - if this is set to true and the database is shutdown
1967: * it will result in total loss of data.
1968: */
1969: public final void setDeleteOnShutdown(boolean status) {
1970: delete_on_shutdown = status;
1971: }
1972:
1973: /**
1974: * Returns true if the database is initialised.
1975: */
1976: public boolean isInitialized() {
1977: return initialised;
1978: }
1979:
1980: /**
1981: * Copies all the persistent data in this database (the conglomerate) to the
1982: * given destination path. This can copy information while the database
1983: * is 'live'.
1984: */
1985: public void liveCopyTo(File path) throws IOException {
1986: if (initialised == false) {
1987: throw new Error("The database is not initialized.");
1988: }
1989:
1990: // Set up the destination conglomerate to copy all the data to,
1991: // Note that this sets up a typical destination conglomerate and changes
1992: // the cache size and disables the debug log.
1993: TransactionSystem copy_system = new TransactionSystem();
1994: DefaultDBConfig config = new DefaultDBConfig();
1995: config.setDatabasePath(path.getAbsolutePath());
1996: config.setLogPath("");
1997: config.setMinimumDebugLevel(50000);
1998: // Set data cache to 1MB
1999: config.setValue("data_cache_size", "1048576");
2000: // Set io_safety_level to 1 for destination database
2001: // ISSUE: Is this a good assumption to make -
2002: // we don't care if changes are lost by a power failure when we are
2003: // backing up the database. Even if journalling is enabled, a power
2004: // failure will lose changes in the backup copy anyway.
2005: config.setValue("io_safety_level", "1");
2006: java.io.StringWriter debug_output = new java.io.StringWriter();
2007: copy_system.setDebugOutput(debug_output);
2008: copy_system.init(config);
2009: final TableDataConglomerate dest_conglomerate = new TableDataConglomerate(
2010: copy_system, copy_system.storeSystem());
2011:
2012: // Open the congloemrate
2013: dest_conglomerate.minimalCreate("DefaultDatabase");
2014:
2015: try {
2016: // Make a copy of this conglomerate into the destination conglomerate,
2017: conglomerate.liveCopyTo(dest_conglomerate);
2018: } finally {
2019: // Close the congloemrate when finished.
2020: dest_conglomerate.close();
2021: // Dispose the TransactionSystem
2022: copy_system.dispose();
2023: }
2024:
2025: }
2026:
2027: // ---------- Database convertion ----------
2028:
2029: /**
2030: * Processes each table in user space and converts the format to the newest
2031: * version of the data file format. This is simply achieved by running the
2032: * 'compactTable' command on the transaction for each table.
2033: */
2034: private void convertAllUserTables(DatabaseConnection connection,
2035: PrintStream out) throws TransactionException {
2036: out.println("Converting user table format to latest version.");
2037: // Convert all user tables in the database
2038: TableName[] all_tables = connection.getTableList();
2039: for (int i = 0; i < all_tables.length; ++i) {
2040: TableName table_name = all_tables[i];
2041: String schema_name = table_name.getSchema();
2042: if (!schema_name.equals("SYS_INFO")
2043: && connection.getTableType(table_name).equals(
2044: "TABLE")) {
2045: out.println("Converting: " + table_name);
2046: connection.compactTable(table_name);
2047: connection.commit();
2048: }
2049: }
2050: }
2051:
2052: /**
2053: * Returns true if the given sql type is possibly a large object.
2054: */
2055: private static boolean largeObjectTest(int sql_type) {
2056: return (sql_type == SQLTypes.CHAR
2057: || sql_type == SQLTypes.VARCHAR
2058: || sql_type == SQLTypes.LONGVARCHAR
2059: || sql_type == SQLTypes.BINARY
2060: || sql_type == SQLTypes.VARBINARY
2061: || sql_type == SQLTypes.LONGVARBINARY
2062: || sql_type == SQLTypes.BLOB || sql_type == SQLTypes.CLOB);
2063: }
2064:
2065: /**
2066: * Scans all the user tables for large objects and if a large object is
2067: * found, it is moved into the BlobStore. A large object is an object that
2068: * uses more than 16 kbytes of storage space.
2069: */
2070: private void moveLargeObjectsToBlobStore(
2071: DatabaseConnection connection, PrintStream out)
2072: throws TransactionException, IOException, DatabaseException {
2073: out.println("Scanning user tables for large objects.");
2074:
2075: DatabaseQueryContext context = new DatabaseQueryContext(
2076: connection);
2077: BlobStore blob_store = conglomerate.getBlobStore();
2078:
2079: // Scan all user tables in the database
2080: TableName[] all_tables = connection.getTableList();
2081: for (int i = 0; i < all_tables.length; ++i) {
2082: TableName table_name = all_tables[i];
2083: String schema_name = table_name.getSchema();
2084: boolean table_changed = false;
2085:
2086: if (!schema_name.equals("SYS_INFO")
2087: && connection.getTableType(table_name).equals(
2088: "TABLE")) {
2089:
2090: out.println("Processing: " + table_name);
2091: DataTable table = connection.getTable(table_name);
2092: DataTableDef table_def = table.getDataTableDef();
2093:
2094: boolean possibly_has_large_objects = false;
2095: int column_count = table_def.columnCount();
2096: for (int n = 0; n < column_count; ++n) {
2097: int sql_type = table_def.columnAt(n).getSQLType();
2098: if (largeObjectTest(sql_type)) {
2099: possibly_has_large_objects = true;
2100: }
2101: }
2102:
2103: if (possibly_has_large_objects) {
2104:
2105: RowEnumeration e = table.rowEnumeration();
2106: while (e.hasMoreRows()) {
2107:
2108: int row_index = e.nextRowIndex();
2109: ArrayList changes = new ArrayList(4);
2110:
2111: for (int p = 0; p < column_count; ++p) {
2112: DataTableColumnDef col_def = table_def
2113: .columnAt(p);
2114: int sql_type = col_def.getSQLType();
2115:
2116: if (largeObjectTest(sql_type)) {
2117: TObject tob = table.getCellContents(p,
2118: row_index);
2119: Object ob = tob.getObject();
2120: if (ob != null) {
2121: // String type
2122: if (ob instanceof StringObject) {
2123: StringObject s_object = (StringObject) ob;
2124: if (s_object.length() > 4 * 1024) {
2125: ClobRef ref = blob_store
2126: .putStringInBlobStore(s_object
2127: .toString());
2128: changes
2129: .add(new Assignment(
2130: new Variable(
2131: table_name,
2132: col_def
2133: .getName()),
2134: new Expression(
2135: new TObject(
2136: tob
2137: .getTType(),
2138: ref))));
2139: }
2140: }
2141: // Binary type
2142: if (ob instanceof ByteLongObject) {
2143: ByteLongObject b_object = (ByteLongObject) ob;
2144: if (b_object.length() > 8 * 1024) {
2145: BlobRef ref = blob_store
2146: .putByteLongObjectInBlobStore(b_object);
2147: changes
2148: .add(new Assignment(
2149: new Variable(
2150: table_name,
2151: col_def
2152: .getName()),
2153: new Expression(
2154: new TObject(
2155: tob
2156: .getTType(),
2157: ref))));
2158: }
2159: }
2160: }
2161: }
2162: }
2163:
2164: // If there was a change
2165: if (changes.size() > 0) {
2166: // Update the row
2167: Assignment[] assignments = (Assignment[]) changes
2168: .toArray(new Assignment[changes
2169: .size()]);
2170: Table st = table.singleRowSelect(row_index);
2171: table.update(context, st, assignments, -1);
2172: table_changed = true;
2173: }
2174:
2175: } // For each row
2176:
2177: if (table_changed) {
2178: // Commit the connection.
2179: connection.commit();
2180: // Compact this table (will remove space from large objects).
2181: connection.compactTable(table_name);
2182: }
2183:
2184: // Commit the connection.
2185: connection.commit();
2186:
2187: }
2188: }
2189: }
2190: }
2191:
2192: /**
2193: * Functionality for converting and old database format to the existing
2194: * format. This would typically be called from a convert tool program.
2195: * <p>
2196: * Returns true if the convert was successful or false if it wasn't (error
2197: * message is output to the PrintWriter).
2198: */
2199: public boolean convertToCurrent(PrintStream out,
2200: String admin_username) throws IOException {
2201:
2202: // Reset all session statistics.
2203: stats().resetSession();
2204:
2205: try {
2206: // Don't log commands (there shouldn't be any anyway).
2207: commands_log = Log.nullLog();
2208:
2209: // Convert the state file if it is necessary.
2210: File legacy_state_file = new File(system.getDatabasePath(),
2211: getName() + ".sf");
2212: if (legacy_state_file.exists()) {
2213: String state_store_fn = getName() + "_sf";
2214: // If the state store file already exists
2215: if (storeSystem().storeExists(state_store_fn)) {
2216: throw new IOException(
2217: "Both legacy and version 1 state file exist. Please remove one.");
2218: }
2219: out
2220: .println("Converting state file to current version.");
2221: // Create the new store,
2222: Store new_ss = storeSystem()
2223: .createStore(state_store_fn);
2224: StateStore ss = new StateStore(new_ss);
2225: // Convert the existing store
2226: long new_p = ss.convert(legacy_state_file, Debug());
2227: // Set the fixed area in the store to point to this new structure
2228: MutableArea fixed_area = new_ss.getMutableArea(-1);
2229: fixed_area.putLong(new_p);
2230: fixed_area.checkOut();
2231: // Flush the changes to the new store and close
2232: storeSystem().closeStore(new_ss);
2233: // Delete the old state file.
2234: legacy_state_file.delete();
2235: out.println("State store written.");
2236: }
2237:
2238: out.println("Opening conglomerate.");
2239:
2240: // Open the conglomerate
2241: conglomerate.open(getName());
2242:
2243: // Check the state of the conglomerate,
2244: DatabaseConnection connection = createNewConnection(null,
2245: null);
2246: DatabaseQueryContext context = new DatabaseQueryContext(
2247: connection);
2248: connection.getLockingMechanism().setMode(
2249: LockingMechanism.EXCLUSIVE_MODE);
2250: if (!connection
2251: .tableExists(TableDataConglomerate.PERSISTENT_VAR_TABLE)) {
2252: out
2253: .println("The sUSRDatabaseVars table doesn't exist. This means the "
2254: + "database is pre-schema version 1 or the table has been deleted."
2255: + "If you are converting an old version of the database, please "
2256: + "convert the database using an older release.");
2257: return false;
2258: }
2259:
2260: // Check the user given exists
2261: if (!userExists(context, admin_username)) {
2262: out
2263: .println("The admin username given ("
2264: + admin_username
2265: + ") does not exist in this database so I am unable to convert the "
2266: + "database.");
2267: return false;
2268: }
2269:
2270: // What version is the data?
2271: DataTable database_vars = connection
2272: .getTable(TableDataConglomerate.PERSISTENT_VAR_TABLE);
2273: Map vars = database_vars.toMap();
2274: String db_version = vars.get("database.version").toString();
2275: if (db_version.equals("1.0")) {
2276: // Convert from 1.0 to 1.4
2277: out.println("Version 1.0 found.");
2278: out
2279: .println("Converting database to version 1.4 schema...");
2280:
2281: try {
2282: // Drop the tables that were deprecated
2283: connection.dropTable(new TableName(SYSTEM_SCHEMA,
2284: "sUSRPrivAdd"));
2285: connection.dropTable(new TableName(SYSTEM_SCHEMA,
2286: "sUSRPrivAlter"));
2287: connection.dropTable(new TableName(SYSTEM_SCHEMA,
2288: "sUSRPrivRead"));
2289: } catch (Error e) { /* ignore */
2290: }
2291:
2292: // Reset the sequence id for the tables.
2293: conglomerate.resetAllSystemTableID();
2294:
2295: // Create/Update the conglomerate level tables.
2296: conglomerate.updateSystemTableSchema();
2297:
2298: // Commit the changes so far.
2299: connection.commit();
2300:
2301: // Create/Update the system tables that are present in every
2302: // conglomerate.
2303: createSystemTables(connection);
2304:
2305: // Commit the changes so far.
2306: connection.commit();
2307:
2308: // Creating the system JDBC system schema
2309: connection.createSchema(JDBC_SCHEMA, "SYSTEM");
2310: // Create the system views
2311: createSystemViews(connection);
2312:
2313: // Sets the system grants for the administrator
2314: setSystemGrants(connection, admin_username);
2315: // Sets the table grants for the administrator
2316: convertPreGrant(connection, admin_username);
2317:
2318: // Allow all localhost TCP connections.
2319: // NOTE: Permissive initial security!
2320: grantHostAccessToUser(context, admin_username, "TCP",
2321: "%");
2322: // Allow all Local connections (from within JVM).
2323: grantHostAccessToUser(context, admin_username, "Local",
2324: "%");
2325:
2326: // Convert all tables in the database to the current table format.
2327: convertAllUserTables(connection, out);
2328:
2329: // Move any large binary or string objects into the blob store.
2330: moveLargeObjectsToBlobStore(connection, out);
2331:
2332: // Set all default system procedures.
2333: setupSystemFunctions(connection, admin_username);
2334:
2335: // Commit the changes so far.
2336: connection.commit();
2337:
2338: // Update to version 1.4
2339: database_vars = connection
2340: .getTable(TableDataConglomerate.PERSISTENT_VAR_TABLE);
2341: updateDatabaseVars(context, database_vars,
2342: "database.version", "1.4");
2343: db_version = "1.4";
2344:
2345: }
2346:
2347: else if (db_version.equals("1.1")) {
2348: // Convert from 1.1 to 1.4
2349: out.println("Version 1.1 found.");
2350: out
2351: .println("Converting database to version 1.4 schema...");
2352:
2353: // Reset the sequence id for the tables.
2354: conglomerate.resetAllSystemTableID();
2355:
2356: // Create/Update the conglomerate level tables.
2357: conglomerate.updateSystemTableSchema();
2358:
2359: // Commit the changes so far.
2360: connection.commit();
2361:
2362: // Create/Update the system tables that are present in every
2363: // conglomerate.
2364: createSystemTables(connection);
2365:
2366: // Commit the changes so far.
2367: connection.commit();
2368: // Update the 'database_vars' table.
2369: database_vars = connection
2370: .getTable(TableDataConglomerate.PERSISTENT_VAR_TABLE);
2371:
2372: // Creating the system JDBC system schema
2373: connection.createSchema(JDBC_SCHEMA, "SYSTEM");
2374: // Create the system views
2375: createSystemViews(connection);
2376:
2377: // Clear all grants.
2378: clearAllGrants(connection);
2379:
2380: // Sets the system grants for the administrator
2381: setSystemGrants(connection, admin_username);
2382: // Sets the table grants for the administrator
2383: convertPreGrant(connection, admin_username);
2384:
2385: // Convert all tables in the database to the current table format.
2386: convertAllUserTables(connection, out);
2387:
2388: // Move any large binary or string objects into the blob store.
2389: moveLargeObjectsToBlobStore(connection, out);
2390:
2391: // Set all default system procedures.
2392: setupSystemFunctions(connection, admin_username);
2393:
2394: // Commit the changes so far.
2395: connection.commit();
2396:
2397: // Update to version 1.4
2398: database_vars = connection
2399: .getTable(TableDataConglomerate.PERSISTENT_VAR_TABLE);
2400: updateDatabaseVars(context, database_vars,
2401: "database.version", "1.4");
2402: db_version = "1.4";
2403:
2404: }
2405:
2406: else if (db_version.equals("1.2")) {
2407: // Convert from 1.2 to 1.4
2408: out.println("Version 1.2 found.");
2409: out
2410: .println("Converting database to version 1.4 schema...");
2411:
2412: // Create/Update the conglomerate level tables.
2413: conglomerate.updateSystemTableSchema();
2414:
2415: // Commit the changes so far.
2416: connection.commit();
2417:
2418: // Create/Update the system tables that are present in every
2419: // conglomerate.
2420: createSystemTables(connection);
2421:
2422: // Commit the changes so far.
2423: connection.commit();
2424:
2425: // Convert all tables in the database to the current table format.
2426: convertAllUserTables(connection, out);
2427:
2428: // Move any large binary or string objects into the blob store.
2429: moveLargeObjectsToBlobStore(connection, out);
2430:
2431: // Commit the changes so far.
2432: connection.commit();
2433:
2434: // Set all default system procedures.
2435: setupSystemFunctions(connection, admin_username);
2436:
2437: // Commit the changes so far.
2438: connection.commit();
2439:
2440: // Update to version 1.4
2441: database_vars = connection
2442: .getTable(TableDataConglomerate.PERSISTENT_VAR_TABLE);
2443: updateDatabaseVars(context, database_vars,
2444: "database.version", "1.4");
2445: db_version = "1.4";
2446:
2447: }
2448:
2449: else if (db_version.equals("1.3")) {
2450: out.println("Version 1.3 found.");
2451: out
2452: .println("Converting database to version 1.4 schema...");
2453:
2454: // Create/Update the conglomerate level tables.
2455: conglomerate.updateSystemTableSchema();
2456:
2457: // Commit the changes so far.
2458: connection.commit();
2459:
2460: // Create/Update the system tables that are present in every
2461: // conglomerate.
2462: createSystemTables(connection);
2463:
2464: // Commit the changes so far.
2465: connection.commit();
2466:
2467: // Drop the 'sUSRSystemTrigger' table that was erroniously added in 1.3
2468: try {
2469: connection.dropTable(new TableName(SYSTEM_SCHEMA,
2470: "sUSRSystemTrigger"));
2471: } catch (Error e) { /* ignore */
2472: }
2473:
2474: // Set all default system procedures.
2475: setupSystemFunctions(connection, admin_username);
2476:
2477: // Commit the changes so far.
2478: connection.commit();
2479:
2480: // Update to version 1.4
2481: database_vars = connection
2482: .getTable(TableDataConglomerate.PERSISTENT_VAR_TABLE);
2483: updateDatabaseVars(context, database_vars,
2484: "database.version", "1.4");
2485: db_version = "1.4";
2486:
2487: }
2488:
2489: else if (db_version.equals("1.4")) {
2490: out.println("Version 1.4 found.");
2491: out.println("Version of data files is current.");
2492: }
2493:
2494: else if (!db_version.equals("1.4")) {
2495: // This means older versions of the database will not support the data
2496: // format of newer versions.
2497: out.println("Version " + db_version + " found.");
2498: out
2499: .println("This is not a recognized version number and can not be "
2500: + "converted. Perhaps this is a future version? I can "
2501: + "not convert backwards from a future version.");
2502: return false;
2503: }
2504:
2505: // Commit and close the connection.
2506: connection.commit();
2507: connection.getLockingMechanism().finishMode(
2508: LockingMechanism.EXCLUSIVE_MODE);
2509: connection.close();
2510: return true;
2511:
2512: } catch (TransactionException e) {
2513: // This would be very strange error to receive for in initializing
2514: // database...
2515: out.println("Transaction Error: " + e.getMessage());
2516: e.printStackTrace(out);
2517: return false;
2518: } catch (DatabaseException e) {
2519: out.println("Database Error: " + e.getMessage());
2520: e.printStackTrace(out);
2521: return false;
2522: }
2523:
2524: finally {
2525: try {
2526: conglomerate.close();
2527: } catch (Throwable e) {
2528: // ignore
2529: }
2530: }
2531:
2532: }
2533:
2534: // ---------- Server side procedures ----------
2535:
2536: /**
2537: * Resolves a procedure name into a DBProcedure object. This is used for
2538: * finding a server side script. It throws a DatabaseException if the
2539: * procedure could not be resolved or there was an error retrieving it.
2540: * <p>
2541: * ISSUE: Move this to DatabaseSystem?
2542: */
2543: public DatabaseProcedure getDBProcedure(String procedure_name,
2544: DatabaseConnection connection) throws DatabaseException {
2545:
2546: // The procedure we are getting.
2547: DatabaseProcedure procedure_instance;
2548:
2549: // See if we can find the procedure as a .js (JavaScript) file in the
2550: // procedure resources.
2551: String p = "/" + procedure_name.replace('.', '/');
2552: // If procedure doesn't starts with '/com/mckoi/procedure/' then add it
2553: // on here.
2554: if (!p.startsWith("/com/mckoi/procedure/")) {
2555: p = "/com/mckoi/procedure/" + p;
2556: }
2557: p = p + ".js";
2558:
2559: // Is there a resource available?
2560: java.net.URL url = getClass().getResource(p);
2561:
2562: if (url != null) {
2563: // Create a server side procedure for the .js file
2564: // ( This code is not included in the GPL release )
2565: procedure_instance = null;
2566:
2567: } else {
2568: try {
2569: // Couldn't find the javascript script, so try and resolve as an
2570: // actual Java class file.
2571: // Find the procedure
2572: Class proc = Class.forName("com.mckoi.procedure."
2573: + procedure_name);
2574: // Instantiate a new instance of the procedure
2575: procedure_instance = (DatabaseProcedure) proc
2576: .newInstance();
2577:
2578: Debug().write(
2579: Lvl.INFORMATION,
2580: this ,
2581: "Getting raw Java class file: "
2582: + procedure_name);
2583: } catch (IllegalAccessException e) {
2584: Debug().writeException(e);
2585: throw new DatabaseException("Illegal Access: "
2586: + e.getMessage());
2587: } catch (InstantiationException e) {
2588: Debug().writeException(e);
2589: throw new DatabaseException("Instantiation Error: "
2590: + e.getMessage());
2591: } catch (ClassNotFoundException e) {
2592: Debug().writeException(e);
2593: throw new DatabaseException("Class Not Found: "
2594: + e.getMessage());
2595: }
2596: }
2597:
2598: // Return the procedure.
2599: return procedure_instance;
2600:
2601: }
2602:
2603: // ---------- System access ----------
2604:
2605: /**
2606: * Returns the DatabaseSystem that this Database is from.
2607: */
2608: public final DatabaseSystem getSystem() {
2609: return system;
2610: }
2611:
2612: /**
2613: * Returns the StoreSystem for this Database.
2614: */
2615: public final StoreSystem storeSystem() {
2616: return system.storeSystem();
2617: }
2618:
2619: /**
2620: * Convenience static for accessing the global Stats object. Perhaps this
2621: * should be deprecated?
2622: */
2623: public final Stats stats() {
2624: return getSystem().stats();
2625: }
2626:
2627: /**
2628: * Returns the DebugLogger implementation from the DatabaseSystem.
2629: */
2630: public final DebugLogger Debug() {
2631: return getSystem().Debug();
2632: }
2633:
2634: /**
2635: * Returns the system trigger manager.
2636: */
2637: public final TriggerManager getTriggerManager() {
2638: return trigger_manager;
2639: }
2640:
2641: /**
2642: * Returns the system user manager.
2643: */
2644: public final UserManager getUserManager() {
2645: return getSystem().getUserManager();
2646: }
2647:
2648: /**
2649: * Creates an event for the database dispatcher.
2650: */
2651: public final Object createEvent(Runnable runner) {
2652: return getSystem().createEvent(runner);
2653: }
2654:
2655: /**
2656: * Posts an event on the database dispatcher.
2657: */
2658: public final void postEvent(int time, Object event) {
2659: getSystem().postEvent(time, event);
2660: }
2661:
2662: /**
2663: * Returns the system DataCellCache.
2664: */
2665: public final DataCellCache getDataCellCache() {
2666: return getSystem().getDataCellCache();
2667: }
2668:
2669: /**
2670: * Returns true if the database has shut down.
2671: */
2672: public final boolean hasShutDown() {
2673: return getSystem().hasShutDown();
2674: }
2675:
2676: /**
2677: * Starts the shutdown thread which should contain delegates that shut the
2678: * database and all its resources down. This method returns immediately.
2679: */
2680: public final void startShutDownThread() {
2681: getSystem().startShutDownThread();
2682: }
2683:
2684: /**
2685: * Blocks until the database has shut down.
2686: */
2687: public final void waitUntilShutdown() {
2688: getSystem().waitUntilShutdown();
2689: }
2690:
2691: /**
2692: * Executes database functions from the 'run' method of the given runnable
2693: * instance on the first available worker thread. All database functions
2694: * must go through a worker thread. If we ensure this, we can easily stop
2695: * all database functions from executing if need be. Also, we only need to
2696: * have a certain number of threads active at any one time rather than a
2697: * unique thread for each connection.
2698: */
2699: public final void execute(User user, DatabaseConnection database,
2700: Runnable runner) {
2701: getSystem().execute(user, database, runner);
2702: }
2703:
2704: /**
2705: * Registers the delegate that is executed when the shutdown thread is
2706: * activated.
2707: */
2708: public final void registerShutDownDelegate(Runnable delegate) {
2709: getSystem().registerShutDownDelegate(delegate);
2710: }
2711:
2712: /**
2713: * Controls whether the database is allowed to execute commands or not. If
2714: * this is set to true, then calls to 'execute' will be executed
2715: * as soon as there is a free worker thread available. Otherwise no
2716: * commands are executed until this is enabled.
2717: */
2718: public final void setIsExecutingCommands(boolean status) {
2719: getSystem().setIsExecutingCommands(status);
2720: }
2721:
2722: /**
2723: * Returns a static table that has a single row but no columns. This table
2724: * is useful for certain database operations.
2725: */
2726: public final Table getSingleRowTable() {
2727: return SINGLE_ROW_TABLE;
2728: }
2729:
2730: // ---------- Static methods ----------
2731:
2732: /**
2733: * Given the sUSRDatabaseVars table, this will update the given key with
2734: * the given value in the table in the current transaction.
2735: */
2736: private static void updateDatabaseVars(QueryContext context,
2737: DataTable database_vars, String key, String value)
2738: throws DatabaseException {
2739: // The references to the first and second column (key/value)
2740: Variable c1 = database_vars.getResolvedVariable(0); // First column
2741: Variable c2 = database_vars.getResolvedVariable(1); // Second column
2742:
2743: // Assignment: second column = value
2744: Assignment assignment = new Assignment(c2, new Expression(
2745: TObject.stringVal(value)));
2746: // All rows from database_vars where first column = the key
2747: Table t1 = database_vars.simpleSelect(context, c1, Operator
2748: .get("="), new Expression(TObject.stringVal(key)));
2749:
2750: // Update the variable
2751: database_vars.update(context, t1,
2752: new Assignment[] { assignment }, -1);
2753:
2754: }
2755:
2756: public void finalize() throws Throwable {
2757: super .finalize();
2758: if (isInitialized()) {
2759: System.err
2760: .println("Database object was finalized and is initialized!");
2761: }
2762: }
2763:
2764: }
|