0001: /*
0002: * $Id: BaseDatabase.java,v 1.97 2007/11/13 19:04:02 rwald Exp $
0003: * =======================================================================
0004: * Copyright (c) 2002-2006 Axion Development Team. All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: *
0010: * 1. Redistributions of source code must retain the above
0011: * copyright notice, this list of conditions and the following
0012: * disclaimer.
0013: *
0014: * 2. Redistributions in binary form must reproduce the above copyright
0015: * notice, this list of conditions and the following disclaimer in
0016: * the documentation and/or other materials provided with the
0017: * distribution.
0018: *
0019: * 3. The names "Tigris", "Axion", nor the names of its contributors may
0020: * not be used to endorse or promote products derived from this
0021: * software without specific prior written permission.
0022: *
0023: * 4. Products derived from this software may not be called "Axion", nor
0024: * may "Tigris" or "Axion" appear in their names without specific prior
0025: * written permission.
0026: *
0027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0028: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
0030: * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0031: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0032: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0033: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0034: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0035: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0036: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0037: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0038: * =======================================================================
0039: */
0040:
0041: package org.axiondb.engine;
0042:
0043: import java.io.File;
0044: import java.io.FileInputStream;
0045: import java.io.IOException;
0046: import java.io.InputStream;
0047: import java.sql.DatabaseMetaData;
0048: import java.util.ArrayList;
0049: import java.util.Enumeration;
0050: import java.util.HashMap;
0051: import java.util.Iterator;
0052: import java.util.List;
0053: import java.util.Map;
0054: import java.util.Properties;
0055:
0056: import org.apache.commons.logging.Log;
0057: import org.apache.commons.logging.LogFactory;
0058: import org.axiondb.AxionException;
0059: import org.axiondb.Column;
0060: import org.axiondb.ColumnIdentifier;
0061: import org.axiondb.DataType;
0062: import org.axiondb.DataTypeFactory;
0063: import org.axiondb.Database;
0064: import org.axiondb.DatabaseLink;
0065: import org.axiondb.ExternalTable;
0066: import org.axiondb.FunctionFactory;
0067: import org.axiondb.Index;
0068: import org.axiondb.IndexFactory;
0069: import org.axiondb.Literal;
0070: import org.axiondb.Row;
0071: import org.axiondb.Selectable;
0072: import org.axiondb.Sequence;
0073: import org.axiondb.Table;
0074: import org.axiondb.TableFactory;
0075: import org.axiondb.TableIdentifier;
0076: import org.axiondb.TransactionManager;
0077: import org.axiondb.engine.commands.DeleteCommand;
0078: import org.axiondb.engine.commands.SubSelectCommand;
0079: import org.axiondb.engine.indexes.BaseBTreeIndex;
0080: import org.axiondb.engine.metaupdaters.AxionColumnsMetaTableUpdater;
0081: import org.axiondb.engine.metaupdaters.AxionDBLinksMetaTableUpdater;
0082: import org.axiondb.engine.metaupdaters.AxionSequencesMetaTableUpdater;
0083: import org.axiondb.engine.metaupdaters.AxionTablePropertiesMetaTableUpdater;
0084: import org.axiondb.engine.metaupdaters.AxionTablesMetaTableUpdater;
0085: import org.axiondb.engine.metaupdaters.AxionTypesMetaTableUpdater;
0086: import org.axiondb.engine.rows.SimpleRow;
0087: import org.axiondb.engine.tables.ExternalAxionDBTable;
0088: import org.axiondb.engine.tables.ExternalDatabaseTable;
0089: import org.axiondb.engine.tables.TableView;
0090: import org.axiondb.event.DatabaseLinkEvent;
0091: import org.axiondb.event.DatabaseModificationListener;
0092: import org.axiondb.event.DatabaseModifiedEvent;
0093: import org.axiondb.event.DatabaseSequenceEvent;
0094: import org.axiondb.event.DatabaseTypeEvent;
0095: import org.axiondb.event.SequenceModificationListener;
0096: import org.axiondb.event.TableModificationListener;
0097: import org.axiondb.functions.ConcreteFunction;
0098: import org.axiondb.functions.FunctionIdentifier;
0099: import org.axiondb.types.BooleanType;
0100: import org.axiondb.types.DateType;
0101: import org.axiondb.types.IntegerType;
0102: import org.axiondb.types.LOBType;
0103: import org.axiondb.types.ShortType;
0104: import org.axiondb.types.StringType;
0105: import org.axiondb.types.TimeType;
0106: import org.axiondb.types.TimestampType;
0107:
0108: /**
0109: * Abstract base {@link Database}implementation.
0110: *
0111: * @version $Revision: 1.97 $ $Date: 2007/11/13 19:04:02 $
0112: * @author Chuck Burdick
0113: * @author Rodney Waldhoff
0114: * @author Morgan Delagrange
0115: * @author James Strachan
0116: * @author Amrish Lal
0117: * @author Rahul Dwivedi
0118: * @author Dave Pekarek Krohn
0119: * @author Ahimanikya Satapathy
0120: */
0121: public abstract class BaseDatabase implements Database {
0122:
0123: public BaseDatabase(String name) {
0124: _name = name;
0125: }
0126:
0127: public void addDatabaseModificationListener(
0128: DatabaseModificationListener l) {
0129: _listeners.add(l);
0130: }
0131:
0132: public void addIndex(Index index, Table table)
0133: throws AxionException {
0134: addIndex(index, table, false);
0135: }
0136:
0137: public void addIndex(Index index, Table table, boolean doPopulate)
0138: throws AxionException {
0139: if (_indices.containsKey(index.getName())) {
0140: throw new AxionException("An index named "
0141: + index.getName() + " already exists");
0142: }
0143:
0144: Column indexedColumn = index.getIndexedColumn();
0145: if (table.isColumnIndexed(indexedColumn)) {
0146: Iterator i = table.getIndices();
0147: while (i.hasNext()) {
0148: Index existing = (Index) i.next();
0149: if (existing.getIndexedColumn().equals(indexedColumn)
0150: && index.getClass() == existing.getClass()) {
0151: throw new AxionException("Column " + indexedColumn
0152: + " is already indexed "
0153: + "by an existing index, "
0154: + existing.getName() + ", of the same type");
0155: }
0156: }
0157: }
0158:
0159: table.addIndex(index);
0160: if (doPopulate) {
0161: try {
0162: table.populateIndex(index);
0163: } catch (AxionException e) {
0164: try {
0165: table.removeIndex(index);
0166: } catch (AxionException ignore) {
0167: // Don't throw this exception; throw the initial one that caused us to
0168: // rollback the index.
0169: }
0170: throw e;
0171: }
0172: }
0173:
0174: _indices.put(index.getName(), new Object[] { index, table });
0175: addIndexMetaEntry(index, table);
0176: }
0177:
0178: public void addTable(Table t) throws AxionException {
0179: if (t != null) {
0180: String name = t.getName();
0181: if (_tables.containsKey(name)) {
0182: throw new AxionException("A table named " + name
0183: + " already exists.");
0184: }
0185:
0186: _tables.put(name, t);
0187: t
0188: .addTableModificationListener((TableModificationListener) _colUpd);
0189: Iterator i = getDatabaseModificationListeners().iterator();
0190: while (i.hasNext()) {
0191: DatabaseModificationListener cur = (DatabaseModificationListener) i
0192: .next();
0193: cur.tableAdded(new DatabaseModifiedEvent(t));
0194: }
0195: }
0196: }
0197:
0198: public void checkpoint() throws AxionException {
0199: for (Iterator tables = _tables.values().iterator(); tables
0200: .hasNext();) {
0201: Table table = (Table) (tables.next());
0202: table.checkpoint();
0203: }
0204: }
0205:
0206: public void createDatabaseLink(DatabaseLink dblink)
0207: throws AxionException {
0208: String upName = dblink.getName().toUpperCase();
0209: _databaseLink.put(upName, dblink);
0210:
0211: DatabaseLinkEvent e = new DatabaseLinkEvent(dblink);
0212: Iterator i = getDatabaseModificationListeners().iterator();
0213: while (i.hasNext()) {
0214: DatabaseModificationListener cur = (DatabaseModificationListener) i
0215: .next();
0216: cur.serverAdded(e);
0217: }
0218: }
0219:
0220: public void createSequence(Sequence seq) throws AxionException {
0221: if (seq != null) {
0222: DatabaseSequenceEvent e = new DatabaseSequenceEvent(seq);
0223: Iterator i = getDatabaseModificationListeners().iterator();
0224: while (i.hasNext()) {
0225: DatabaseModificationListener cur = (DatabaseModificationListener) i
0226: .next();
0227: cur.sequenceAdded(e);
0228: }
0229: _sequences.put(seq.getName(), seq);
0230: seq
0231: .addSequenceModificationListener((SequenceModificationListener) _seqUpd);
0232: }
0233: }
0234:
0235: public void dropDatabaseLink(String name) throws AxionException {
0236: String upName = name.toUpperCase();
0237: DatabaseLink dblink = (DatabaseLink) (_databaseLink
0238: .remove(upName));
0239: if (null == dblink) {
0240: throw new AxionException("No Database Link Server "
0241: + upName + " found");
0242: }
0243:
0244: DatabaseLinkEvent e = new DatabaseLinkEvent(dblink);
0245: Iterator i = getDatabaseModificationListeners().iterator();
0246: while (i.hasNext()) {
0247: DatabaseModificationListener cur = (DatabaseModificationListener) i
0248: .next();
0249: cur.serverDropped(e);
0250: }
0251: }
0252:
0253: public void dropDependentExternalDBTable(List tables)
0254: throws AxionException {
0255: Iterator i = tables.iterator();
0256: while (i.hasNext()) {
0257: Table t = (Table) (i.next());
0258: dropTable(t.getName());
0259:
0260: List v = getDependentExternalDBTable(t.getName());
0261: if (!v.isEmpty()) {
0262: dropDependentExternalDBTable(v);
0263: }
0264: }
0265: }
0266:
0267: public void dropDependentViews(List views) throws AxionException {
0268: Iterator i = views.iterator();
0269: while (i.hasNext()) {
0270: Table t = (Table) (i.next());
0271: dropTable(t.getName());
0272:
0273: List v = getDependentViews(t.getName());
0274: if (!v.isEmpty()) {
0275: dropDependentViews(v);
0276: }
0277: }
0278: }
0279:
0280: public void dropIndex(String name) throws AxionException {
0281: String upName = name.toUpperCase();
0282: Object[] pair = (Object[]) (_indices.remove(upName));
0283: if (null == pair) {
0284: throw new AxionException("No index " + upName + " found.");
0285: }
0286:
0287: Index index = (Index) (pair[0]);
0288: Table table = (Table) (pair[1]);
0289: table.removeIndex(index);
0290: removeIndexMetaEntry(index);
0291: }
0292:
0293: public void dropSequence(String name) throws AxionException {
0294: String upName = name.toUpperCase();
0295: Sequence seq = (Sequence) (_sequences.remove(upName));
0296: if (null == seq) {
0297: throw new AxionException("No sequence " + upName + " found");
0298: }
0299:
0300: DatabaseSequenceEvent e = new DatabaseSequenceEvent(seq);
0301: Iterator i = getDatabaseModificationListeners().iterator();
0302: while (i.hasNext()) {
0303: DatabaseModificationListener cur = (DatabaseModificationListener) i
0304: .next();
0305: cur.sequenceDropped(e);
0306: }
0307: }
0308:
0309: public void dropTable(String name) throws AxionException {
0310: String upName = name.toUpperCase();
0311: if (_tables.containsKey(upName)) {
0312: Table table = (Table) (_tables.remove(upName));
0313:
0314: // Copying to local List to avoid ConcurrentModificationException
0315: Iterator<Index> indexIterator = table.getIndices();
0316: List<Index> indexesToDrop = new ArrayList<Index>();
0317: while (indexIterator.hasNext()) {
0318: indexesToDrop.add(indexIterator.next());
0319: }
0320:
0321: indexIterator = indexesToDrop.iterator();
0322: while (indexIterator.hasNext()) {
0323: dropIndex(((Index) indexIterator.next()).getName());
0324: }
0325:
0326: Iterator<DatabaseModificationListener> dbModificationListnerIterator = getDatabaseModificationListeners()
0327: .iterator();
0328: while (dbModificationListnerIterator.hasNext()) {
0329: DatabaseModificationListener cur = (DatabaseModificationListener) dbModificationListnerIterator
0330: .next();
0331: cur.tableDropped(new DatabaseModifiedEvent(table));
0332: }
0333:
0334: table.drop();
0335: } else {
0336: throw new AxionException("No table " + upName + " found");
0337: }
0338: }
0339:
0340: public DatabaseLink getDatabaseLink(String name) {
0341: return (DatabaseLink) _databaseLink.get(name.toUpperCase());
0342: }
0343:
0344: public List getDatabaseModificationListeners() {
0345: return _listeners;
0346: }
0347:
0348: public DataType getDataType(String name) {
0349: DataType type = (DataType) (_dataTypes.get(name.toUpperCase()));
0350: if (type instanceof LOBType) {
0351: type = ((DataTypeFactory) type).makeNewInstance();
0352: }
0353: return type;
0354: }
0355:
0356: // --------------------------------------------------------------- Protected
0357:
0358: public List getDependentExternalDBTable(String name) {
0359: String upName = name.toUpperCase();
0360: Iterator<Table> i = getTables();
0361: List<ExternalTable> tables = new ArrayList<ExternalTable>();
0362: while (i.hasNext()) {
0363: Table t = (Table) (i.next());
0364: if (t instanceof ExternalDatabaseTable) {
0365: ExternalDatabaseTable et = (ExternalDatabaseTable) t;
0366: if (upName.equals(et.getDBLinkName())) {
0367: tables.add(et);
0368: }
0369: } else if (t instanceof ExternalAxionDBTable) {
0370: ExternalAxionDBTable et = (ExternalAxionDBTable) t;
0371: if (upName.equals(et.getDBLinkName())) {
0372: tables.add(et);
0373: }
0374: }
0375: }
0376: return tables;
0377: }
0378:
0379: public List getDependentViews(String tableName) {
0380: String upName = tableName.toUpperCase();
0381: Iterator i = getTables();
0382: List<Table> views = new ArrayList<Table>();
0383: while (i.hasNext()) {
0384: Table t = (Table) (i.next());
0385: if (t instanceof TableView) {
0386: Iterator i2 = ((TableView) t).getTables();
0387: String tName;
0388: while (i2.hasNext()) {
0389: tName = ((TableIdentifier) i2.next())
0390: .getTableName();
0391: if (upName.equals(tName)) {
0392: views.add(t);
0393: break;
0394: }
0395: }
0396: }
0397: }
0398: return views;
0399: }
0400:
0401: public ConcreteFunction getFunction(String name) {
0402: FunctionFactory factory = (FunctionFactory) (_functions
0403: .get(name.toUpperCase()));
0404: if (null != factory) {
0405: return factory.makeNewInstance();
0406: }
0407: return null;
0408: }
0409:
0410: public Object getGlobalVariable(String key) {
0411: return _globalVariables.get(key.toUpperCase());
0412: }
0413:
0414: public IndexFactory getIndexFactory(String name) {
0415: return (IndexFactory) (_indexTypes.get(name.toUpperCase()));
0416: }
0417:
0418: public String getName() {
0419: return _name;
0420: }
0421:
0422: public Sequence getSequence(String name) {
0423: return (Sequence) _sequences.get(name.toUpperCase());
0424: }
0425:
0426: public Table getTable(String name) throws AxionException {
0427: String upName = name.toUpperCase();
0428: return (Table) (_tables.get(upName));
0429: }
0430:
0431: public Table getTable(TableIdentifier table) throws AxionException {
0432: return (Table) (_tables.get(table.getTableName()));
0433: }
0434:
0435: public TableFactory getTableFactory(String name) {
0436: return (TableFactory) (_tableTypes.get(name.toUpperCase()));
0437: }
0438:
0439: public TransactionManager getTransactionManager() {
0440: return _transactionManager;
0441: }
0442:
0443: // ----------------------------------------------------------------- Private
0444:
0445: public boolean hasDatabaseLink(String name) throws AxionException {
0446: return getDatabaseLink(name) != null;
0447: }
0448:
0449: public boolean hasIndex(String name) throws AxionException {
0450: return _indices.containsKey(name);
0451: }
0452:
0453: public boolean hasSequence(String name) throws AxionException {
0454: return getSequence(name) != null;
0455: }
0456:
0457: public boolean hasTable(String name) throws AxionException {
0458: return getTable(name) != null;
0459: }
0460:
0461: public boolean hasTable(TableIdentifier id) throws AxionException {
0462: return getTable(id) != null;
0463: }
0464:
0465: public boolean isReadOnly() {
0466: return _readOnly;
0467: }
0468:
0469: /** Migrate from older version to newer version for this database */
0470: public void migrate(int version) throws AxionException {
0471: }
0472:
0473: public void remount(File newdir) throws AxionException {
0474: for (Iterator tables = _tables.values().iterator(); tables
0475: .hasNext();) {
0476: Table table = (Table) (tables.next());
0477: table.remount(new File(newdir, table.getName()), false);
0478: }
0479: }
0480:
0481: public void removeDatabaseModificationListener(
0482: DatabaseModificationListener l) {
0483: _listeners.remove(l);
0484: }
0485:
0486: public void renameTable(String oldName, String newName)
0487: throws AxionException {
0488: String upOldName = oldName.toUpperCase();
0489: String upNewName = newName.toUpperCase();
0490: if (_tables.containsKey(upOldName)) {
0491: Table table = (Table) (_tables.remove(upOldName));
0492: // Note: this does not handle index migration
0493:
0494: // Initiate table drop event
0495: Iterator i = getDatabaseModificationListeners().iterator();
0496: while (i.hasNext()) {
0497: DatabaseModificationListener cur = (DatabaseModificationListener) i
0498: .next();
0499: cur.tableDropped(new DatabaseModifiedEvent(table));
0500: }
0501:
0502: table.rename(upOldName, upNewName);
0503: _tables.put(upNewName, table);
0504:
0505: // table add event
0506: table
0507: .addTableModificationListener((TableModificationListener) _colUpd);
0508: i = getDatabaseModificationListeners().iterator();
0509: while (i.hasNext()) {
0510: DatabaseModificationListener cur = (DatabaseModificationListener) i
0511: .next();
0512: cur.tableAdded(new DatabaseModifiedEvent(table));
0513: }
0514: } else {
0515: throw new AxionException("No table " + upOldName + " found");
0516: }
0517: }
0518:
0519: // to set the db for subselect this fun
0520: public Selectable resolveSelectSelectable(SubSelectCommand select,
0521: TableIdentifier[] tables) {
0522: select.setDB(this );
0523: select.setParentTables(tables);
0524: return select;
0525: }
0526:
0527: public void shutdown() throws AxionException {
0528: checkpoint();
0529: for (Iterator tables = _tables.values().iterator(); tables
0530: .hasNext();) {
0531: Table table = (Table) (tables.next());
0532: table.shutdown();
0533: }
0534: Databases.forgetDatabase(getName());
0535: }
0536:
0537: // -------------------------------------------------------------- Attributes
0538:
0539: public void tableAltered(Table t) throws AxionException {
0540: Iterator i = getDatabaseModificationListeners().iterator();
0541: while (i.hasNext()) {
0542: DatabaseModificationListener cur = (DatabaseModificationListener) i
0543: .next();
0544: DatabaseModifiedEvent e = new DatabaseModifiedEvent(t);
0545: cur.tableDropped(e);
0546: cur.tableAdded(e);
0547: }
0548: }
0549:
0550: /**
0551: * Should get called by subclasses in constructors
0552: */
0553: protected void createMetaDataTables() throws AxionException {
0554: Table columns = null;
0555: {
0556: columns = createSystemTable("AXION_COLUMNS");
0557: columns
0558: .addColumn(new Column("TABLE_CAT", new StringType()));
0559: columns.addColumn(new Column("TABLE_SCHEM",
0560: new StringType()));
0561: columns
0562: .addColumn(new Column("TABLE_NAME",
0563: new StringType()));
0564: columns.addColumn(new Column("COLUMN_NAME",
0565: new StringType()));
0566: columns.addColumn(new Column("DATA_TYPE", new ShortType()));
0567: columns
0568: .addColumn(new Column("TYPE_NAME", new StringType()));
0569: columns.addColumn(new Column("COLUMN_SIZE",
0570: new IntegerType()));
0571: columns.addColumn(new Column("BUFFER_LENGTH",
0572: new IntegerType()));
0573: columns.addColumn(new Column("DECIMAL_DIGITS",
0574: new IntegerType()));
0575: columns.addColumn(new Column("NUM_PREC_RADIX",
0576: new IntegerType()));
0577: columns
0578: .addColumn(new Column("NULLABLE", new IntegerType()));
0579: columns.addColumn(new Column("REMARKS", new StringType()));
0580: columns
0581: .addColumn(new Column("COLUMN_DEF",
0582: new StringType()));
0583: columns.addColumn(new Column("SQL_DATA_TYPE",
0584: new IntegerType()));
0585: columns.addColumn(new Column("SQL_DATETIME_SUB",
0586: new IntegerType()));
0587: columns.addColumn(new Column("CHAR_OCTET_LENGTH",
0588: new IntegerType()));
0589: columns.addColumn(new Column("ORDINAL_POSITION",
0590: new IntegerType()));
0591: columns.addColumn(new Column("IS_NULLABLE",
0592: new StringType()));
0593: columns.addColumn(new Column("SCOPE_CATALOG",
0594: new StringType()));
0595: columns.addColumn(new Column("SCOPE_SCHEMA",
0596: new StringType()));
0597: columns.addColumn(new Column("SCOPE_TABLE",
0598: new StringType()));
0599: columns.addColumn(new Column("SOURCE_DATA_TYPE",
0600: new ShortType()));
0601: }
0602: addDatabaseModificationListener(_colUpd);
0603: addTable(columns);
0604:
0605: Table tables = null;
0606: AxionTablesMetaTableUpdater updTables = new AxionTablesMetaTableUpdater(
0607: this );
0608: {
0609: tables = createSystemTable("AXION_TABLES");
0610: tables.addColumn(new Column("TABLE_CAT", new StringType()));
0611: tables
0612: .addColumn(new Column("TABLE_SCHEM",
0613: new StringType()));
0614: tables
0615: .addColumn(new Column("TABLE_NAME",
0616: new StringType()));
0617: tables
0618: .addColumn(new Column("TABLE_TYPE",
0619: new StringType()));
0620: tables.addColumn(new Column("REMARKS", new StringType()));
0621: // bootstrap AXION_COLUMNS into AXION_TABLES
0622: Row row = updTables.createRowForAddedTable(columns);
0623: tables.addRow(row);
0624: }
0625: addDatabaseModificationListener(updTables);
0626: addTable(tables);
0627:
0628: {
0629: Table tableTypes = createSystemTable("AXION_TABLE_TYPES");
0630: tableTypes.addColumn(new Column("TABLE_TYPE",
0631: new StringType()));
0632: String[] types = new String[] { Table.REGULAR_TABLE_TYPE,
0633: Table.SYSTEM_TABLE_TYPE };
0634: for (int i = 0; i < types.length; i++) {
0635: SimpleRow row = new SimpleRow(1);
0636: row.set(0, types[i]);
0637: tableTypes.addRow(row);
0638: }
0639: addTable(tableTypes);
0640: }
0641:
0642: {
0643: Table catalogs = createSystemTable("AXION_CATALOGS");
0644: catalogs
0645: .addColumn(new Column("TABLE_CAT", new StringType()));
0646: {
0647: SimpleRow row = new SimpleRow(1);
0648: row.set(0, "");
0649: catalogs.addRow(row);
0650: }
0651: addTable(catalogs);
0652: }
0653:
0654: {
0655: Table schemata = createSystemTable("AXION_SCHEMATA");
0656: schemata
0657: .addColumn(new Column("TABLE_CAT", new StringType()));
0658: schemata.addColumn(new Column("TABLE_SCHEM",
0659: new StringType()));
0660: {
0661: SimpleRow row = new SimpleRow(2);
0662: row.set(0, "");
0663: row.set(1, "");
0664: schemata.addRow(row);
0665: }
0666: addTable(schemata);
0667: }
0668:
0669: {
0670: // FIXME: these are a bit hacked
0671: Table types = createSystemTable("AXION_TYPES");
0672: types.addColumn(new Column("TYPE_NAME", new StringType()));
0673: types.addColumn(new Column("DATA_TYPE", new ShortType()));
0674: types.addColumn(new Column("PRECISION", new IntegerType()));
0675: types.addColumn(new Column("LITERAL_PREFIX",
0676: new StringType()));
0677: types.addColumn(new Column("LITERAL_SUFFIX",
0678: new StringType()));
0679: types.addColumn(new Column("CREATE_PARAMS",
0680: new StringType()));
0681: types.addColumn(new Column("NULLABLE", new IntegerType()));
0682: types.addColumn(new Column("CASE_SENSITIVE",
0683: new BooleanType()));
0684: types.addColumn(new Column("SEARCHABLE", new ShortType()));
0685: types.addColumn(new Column("UNSIGNED_ATTRIBUTE",
0686: new BooleanType()));
0687: types.addColumn(new Column("FIXED_PREC_SCALE",
0688: new BooleanType()));
0689: types.addColumn(new Column("AUTO_INCREMENT",
0690: new BooleanType()));
0691: types.addColumn(new Column("LOCAL_TYPE_NAME",
0692: new StringType()));
0693: types
0694: .addColumn(new Column("MINIMUM_SCALE",
0695: new ShortType()));
0696: types
0697: .addColumn(new Column("MAXIMUM_SCALE",
0698: new ShortType()));
0699: types.addColumn(new Column("SQL_DATA_TYPE",
0700: new IntegerType()));
0701: types.addColumn(new Column("SQL_DATETIME_SUB",
0702: new IntegerType()));
0703: types.addColumn(new Column("NUM_PREC_RADIX",
0704: new IntegerType()));
0705: addTable(types);
0706: addDatabaseModificationListener(new AxionTypesMetaTableUpdater(
0707: this ));
0708: }
0709:
0710: {
0711: Table seqTable = createSystemTable("AXION_SEQUENCES");
0712: seqTable.addColumn(new Column("SEQUENCE_NAME",
0713: new StringType()));
0714: seqTable.addColumn(new Column("SEQUENCE_VALUE",
0715: new IntegerType()));
0716: addTable(seqTable);
0717: addDatabaseModificationListener(_seqUpd);
0718: }
0719:
0720: // Add AXION_TABLE_PROPERTIES to hold values of external table properties
0721: {
0722: Table tableProps = createSystemTable("AXION_TABLE_PROPERTIES");
0723: tableProps.addColumn(new Column("TABLE_NAME",
0724: new StringType()));
0725: tableProps.addColumn(new Column("PROPERTY_NAME",
0726: new StringType()));
0727: tableProps.addColumn(new Column("PROPERTY_VALUE",
0728: new StringType()));
0729: addTable(tableProps);
0730: addDatabaseModificationListener(new AxionTablePropertiesMetaTableUpdater(
0731: this ));
0732: }
0733:
0734: // Add AXION_DB_LINKS to hold references to external database servers
0735: {
0736: Table tableLinks = createSystemTable(BaseDatabase.SYSTABLE_DB_LINKS);
0737: tableLinks.addColumn(new Column("LINK_NAME",
0738: new StringType()));
0739: tableLinks.addColumn(new Column("LINK_URL",
0740: new StringType()));
0741: tableLinks.addColumn(new Column("LINK_USERNAME",
0742: new StringType()));
0743: addTable(tableLinks);
0744: addDatabaseModificationListener(new AxionDBLinksMetaTableUpdater(
0745: this ));
0746: }
0747:
0748: // Add AXION_INDICES to hold information on indexes
0749: {
0750: Table tableIndices = createSystemTable(BaseDatabase.SYSTABLE_INDEX_INFO);
0751: tableIndices.addColumn(new Column("TABLE_CAT",
0752: new StringType()));
0753: tableIndices.addColumn(new Column("TABLE_SCHEM",
0754: new StringType()));
0755: tableIndices.addColumn(new Column("TABLE_NAME",
0756: new StringType()));
0757: tableIndices.addColumn(new Column("NON_UNIQUE",
0758: new BooleanType()));
0759: tableIndices.addColumn(new Column("INDEX_QUALIFIER",
0760: new StringType()));
0761: tableIndices.addColumn(new Column("INDEX_NAME",
0762: new StringType()));
0763: tableIndices.addColumn(new Column("TYPE", new ShortType()));
0764: tableIndices.addColumn(new Column("ORDINAL_POSITION",
0765: new ShortType()));
0766: tableIndices.addColumn(new Column("COLUMN_NAME",
0767: new StringType()));
0768: tableIndices.addColumn(new Column("ASC_OR_DESC",
0769: new StringType()));
0770: tableIndices.addColumn(new Column("CARDINALITY",
0771: new IntegerType()));
0772: tableIndices.addColumn(new Column("PAGES",
0773: new IntegerType()));
0774: tableIndices.addColumn(new Column("FILTER_CONDITION",
0775: new StringType()));
0776: tableIndices.addColumn(new Column("INDEX_TYPE",
0777: new StringType()));
0778: addTable(tableIndices);
0779: }
0780: }
0781:
0782: protected abstract Table createSystemTable(String name);
0783:
0784: protected int getSequenceCount() {
0785: return _sequences.size();
0786: }
0787:
0788: protected Iterator<Sequence> getSequences() {
0789: return _sequences.values().iterator();
0790: }
0791:
0792: protected Iterator<Table> getTables() {
0793: return _tables.values().iterator();
0794: }
0795:
0796: protected void loadProperties(Properties props)
0797: throws AxionException {
0798: Enumeration keys = props.propertyNames();
0799: while (keys.hasMoreElements()) {
0800: String key = (String) (keys.nextElement());
0801: if (key.startsWith("type.")) {
0802: addDataType(key.substring("type.".length()), props
0803: .getProperty(key));
0804: } else if (key.startsWith("function.")) {
0805: addFunction(key.substring("function.".length()), props
0806: .getProperty(key));
0807: } else if (key.startsWith("index.")) {
0808: addIndexType(key.substring("index.".length()), props
0809: .getProperty(key));
0810: } else if (key.startsWith("table.")) {
0811: addTableType(key.substring("table.".length()), props
0812: .getProperty(key));
0813: } else if (key.equals("readonly")
0814: || key.equals("database.readonly")) {
0815: String val = props.getProperty(key);
0816: if ("yes".equalsIgnoreCase(val)
0817: || "true".equalsIgnoreCase(val)
0818: || "on".equalsIgnoreCase(val)) {
0819: _readOnly = true;
0820: } else {
0821: _readOnly = false;
0822: }
0823: } else if (key.startsWith("database.timezone")) {
0824: TimestampType.setTimeZone(props.getProperty(key));
0825: DateType.setTimeZone(props.getProperty(key));
0826: TimeType.setTimeZone(props.getProperty(key));
0827: } else if (key.startsWith("database.")) {
0828: _globalVariables.put(key
0829: .substring("database.".length()).toUpperCase(),
0830: props.getProperty(key));
0831: } else {
0832: _log.warn("Unrecognized property \"" + key + "\".");
0833: }
0834: }
0835: }
0836:
0837: private void addDataType(String typename, DataTypeFactory factory)
0838: throws AxionException {
0839: assertNotNull(typename, factory);
0840: if (null != _dataTypes.get(typename.toUpperCase())) {
0841: throw new AxionException("A type named \"" + typename
0842: + "\" already exists ("
0843: + _dataTypes.get(typename.toUpperCase()) + ")");
0844: }
0845: _log.debug("Adding type \"" + typename + "\" (" + factory
0846: + ").");
0847: DataType type = factory.makeNewInstance();
0848: typename = typename.toUpperCase();
0849: _dataTypes.put(typename, type);
0850: DatabaseTypeEvent e = new DatabaseTypeEvent(typename, type);
0851: Iterator i = getDatabaseModificationListeners().iterator();
0852: while (i.hasNext()) {
0853: DatabaseModificationListener cur = (DatabaseModificationListener) i
0854: .next();
0855: cur.typeAdded(e);
0856: }
0857: }
0858:
0859: private void addDataType(String typename, String factoryclassname)
0860: throws AxionException {
0861: assertNotNull(typename, factoryclassname);
0862: try {
0863: DataTypeFactory factory = (DataTypeFactory) (getInstanceForClassName(factoryclassname));
0864: addDataType(typename, factory);
0865: } catch (ClassCastException e) {
0866: throw new AxionException("Expected DataType for \""
0867: + factoryclassname + "\".");
0868: }
0869: }
0870:
0871: private void addFunction(String fnname, FunctionFactory factory)
0872: throws AxionException {
0873: assertNotNull(fnname, factory);
0874: if (null != _functions.get(fnname.toUpperCase())) {
0875: throw new AxionException("A function named \"" + fnname
0876: + "\" already exists ("
0877: + _functions.get(fnname.toUpperCase()) + ")");
0878: }
0879: _log.debug("Adding function \"" + fnname + "\" (" + factory
0880: + ").");
0881: _functions.put(fnname.toUpperCase(), factory);
0882: }
0883:
0884: private void addFunction(String fnname, String factoryclassname)
0885: throws AxionException {
0886: assertNotNull(fnname, factoryclassname);
0887: try {
0888: FunctionFactory factory = (FunctionFactory) (getInstanceForClassName(factoryclassname));
0889: addFunction(fnname, factory);
0890: } catch (ClassCastException e) {
0891: throw new AxionException("Expected FunctionFactory for \""
0892: + factoryclassname + "\".");
0893: }
0894: }
0895:
0896: /**
0897: * Adds index metadata information to system table for display in console and
0898: * retrieval via JDBC DatabaseMetaData class.
0899: *
0900: * @param index Index to add to system table
0901: * @param table table associated with <code>index</code>
0902: */
0903: private void addIndexMetaEntry(Index index, Table table)
0904: throws AxionException {
0905: // FIXME: TYPE, ASC_OR_DESC, CARDINALITY, and PAGES need to be revisited as
0906: // placeholder values are returned.
0907: Row row = new SimpleRow(14);
0908: row.set(0, null);
0909: row.set(1, null);
0910: row.set(2, table.getName());
0911: row.set(3, Boolean.valueOf(index.isUnique()));
0912: row.set(4, null);
0913: row.set(5, index.getName());
0914: // FIXME: Clarify index types and how they map to those indicated in
0915: // DatabaseMetaData
0916: row.set(6, new Short(DatabaseMetaData.tableIndexOther));
0917: row.set(7, Short.valueOf("1"));
0918: row.set(8, index.getIndexedColumn().getName());
0919: // Determine sort order if any and set accordingly
0920: row.set(9, null);
0921: // Determine number of unique values in index
0922: row.set(10, new Short(Short.MAX_VALUE));
0923: // Determine number of pages used for current index
0924: row.set(11, new Short(Short.MAX_VALUE));
0925: row.set(12, null);
0926: // This column is Axion-specific and indicates the basic indexing strategy
0927: row.set(13, (index instanceof BaseBTreeIndex) ? "BTREE"
0928: : "ARRAY");
0929:
0930: this .getTable(SYSTABLE_INDEX_INFO).addRow(row);
0931: }
0932:
0933: private void addIndexType(String typename, IndexFactory factory)
0934: throws AxionException {
0935: assertNotNull(typename, factory);
0936: if (null != _indexTypes.get(typename.toUpperCase())) {
0937: throw new AxionException("An index type named \""
0938: + typename + "\" already exists ("
0939: + _indexTypes.get(typename.toUpperCase()) + ")");
0940: }
0941: _log.debug("Adding index type \"" + typename + "\" (" + factory
0942: + ").");
0943: _indexTypes.put(typename.toUpperCase(), factory);
0944: }
0945:
0946: private void addIndexType(String typename, String factoryclassname)
0947: throws AxionException {
0948: assertNotNull(typename, factoryclassname);
0949: try {
0950: IndexFactory factory = (IndexFactory) (getInstanceForClassName(factoryclassname));
0951: addIndexType(typename, factory);
0952: } catch (ClassCastException e) {
0953: throw new AxionException("Expected IndexFactory for \""
0954: + factoryclassname + "\".");
0955: }
0956: }
0957:
0958: private void addTableType(String typename, String factoryclassname)
0959: throws AxionException {
0960: if (null == typename || null == factoryclassname) {
0961: throw new AxionException("Neither argument can be null.");
0962: }
0963: try {
0964: TableFactory factory = (TableFactory) (getInstanceForClassName(factoryclassname));
0965: addTableType(typename, factory);
0966: } catch (ClassCastException e) {
0967: throw new AxionException("Expected IndexFactory for \""
0968: + factoryclassname + "\".");
0969: }
0970: }
0971:
0972: private void addTableType(String typename, TableFactory factory)
0973: throws AxionException {
0974: if (null == typename || null == factory) {
0975: throw new AxionException("Neither argument can be null.");
0976: }
0977: if (null != _tableTypes.get(typename.toUpperCase())) {
0978: throw new AxionException("An table type named \""
0979: + typename + "\" already exists ("
0980: + _tableTypes.get(typename.toUpperCase()) + ")");
0981: }
0982: _log.debug("Adding table type \"" + typename + "\" (" + factory
0983: + ").");
0984: _tableTypes.put(typename.toUpperCase(), factory);
0985: }
0986:
0987: private void assertNotNull(Object obj1, Object obj2)
0988: throws AxionException {
0989: if (null == obj1 || null == obj2) {
0990: throw new AxionException("Neither argument can be null.");
0991: }
0992: }
0993:
0994: private Object getInstanceForClassName(String classname)
0995: throws AxionException {
0996: try {
0997: Class clazz = Class.forName(classname);
0998: return clazz.newInstance();
0999: } catch (ClassNotFoundException e) {
1000: throw new AxionException("Class \"" + classname
1001: + "\" not found.");
1002: } catch (InstantiationException e) {
1003: throw new AxionException("Unable to instantiate class \""
1004: + classname + "\" via a no-arg constructor.");
1005: } catch (IllegalAccessException e) {
1006: throw new AxionException(
1007: "IllegalAccessException trying to instantiate class \""
1008: + classname
1009: + "\" via a no-arg constructor.");
1010: }
1011: }
1012:
1013: /**
1014: * @param index
1015: */
1016: private void removeIndexMetaEntry(Index index)
1017: throws AxionException {
1018: FunctionIdentifier fnIndexName = new FunctionIdentifier("=");
1019: fnIndexName.addArgument(new ColumnIdentifier("INDEX_NAME"));
1020: fnIndexName.addArgument(new Literal(index.getName()));
1021:
1022: DeleteCommand cmd = new DeleteCommand(SYSTABLE_INDEX_INFO,
1023: fnIndexName);
1024: try {
1025: cmd.execute(this );
1026: } catch (AxionException ex) {
1027: _log
1028: .error(
1029: "Unable to remove mention of index in system tables",
1030: ex);
1031: }
1032: }
1033:
1034: /** Callers should treat the returned Properties as immutable. */
1035: protected static synchronized Properties getBaseProperties() {
1036: if (null == _props) {
1037: _props = new Properties();
1038: InputStream in = null;
1039: try {
1040: in = getBasePropertyStream();
1041: if (null != in) {
1042: _props.load(in);
1043: } else {
1044: _log
1045: .warn("Could not find axiondb.properties on the classpath.");
1046: }
1047: } catch (Exception e) {
1048: _log.error("Exception while base properties", e); // PROPOGATE UP!?!
1049: } finally {
1050: try {
1051: in.close();
1052: } catch (Exception e) {
1053: }
1054: }
1055: }
1056: return _props;
1057: }
1058:
1059: private static InputStream getBasePropertyStream() {
1060: InputStream in = getBasePropertyStreamFromProperty();
1061: if (null == in) {
1062: in = getBasePropertyStreamFromClassLoader();
1063: }
1064: if (null == in) {
1065: in = getBasePropertyStreamFromContextClassLoader();
1066: }
1067: return in;
1068: }
1069:
1070: private static InputStream getBasePropertyStreamFromClassLoader() {
1071: try {
1072: return getPropertyStream(BaseDatabase.class
1073: .getClassLoader());
1074: } catch (Exception e) {
1075: return null;
1076: }
1077: }
1078:
1079: private static InputStream getBasePropertyStreamFromContextClassLoader() {
1080: try {
1081: return getPropertyStream(Thread.currentThread()
1082: .getContextClassLoader());
1083: } catch (Exception e) {
1084: return null;
1085: }
1086: }
1087:
1088: private static InputStream getBasePropertyStreamFromProperty() {
1089: InputStream in = null;
1090: String propfile = null;
1091: try {
1092: propfile = System
1093: .getProperty("org.axiondb.engine.BaseDatabase.properties");
1094: } catch (Throwable t) {
1095: propfile = null;
1096: }
1097: if (null != propfile) {
1098: try {
1099: File file = new File(propfile);
1100: if (file.exists()) {
1101: in = new FileInputStream(file);
1102: }
1103: } catch (IOException e) {
1104: in = null;
1105: }
1106: }
1107: return in;
1108: }
1109:
1110: private static InputStream getPropertyStream(ClassLoader classLoader) {
1111: InputStream in = null;
1112: if (null != classLoader) {
1113: in = classLoader
1114: .getResourceAsStream("org/axiondb/axiondb.properties");
1115: if (null == in) {
1116: in = classLoader
1117: .getResourceAsStream("axiondb.properties");
1118: }
1119: }
1120: return in;
1121: }
1122:
1123: public static final String SYSTABLE_DB_LINKS = "AXION_DB_LINKS";
1124: public static final String SYSTABLE_INDEX_INFO = "AXION_INDEX_INFO";
1125: private static Log _log = LogFactory.getLog(BaseDatabase.class);
1126: private static Properties _props;
1127:
1128: private Map<String, DatabaseLink> _databaseLink = new HashMap<String, DatabaseLink>();
1129: private Map<String, DataType> _dataTypes = new HashMap<String, DataType>();
1130: private Map<String, FunctionFactory> _functions = new HashMap<String, FunctionFactory>();
1131: private Map<String, String> _globalVariables = new HashMap<String, String>();
1132: private Map<String, IndexFactory> _indexTypes = new HashMap<String, IndexFactory>();
1133: private Map<String, Object[]> _indices = new HashMap<String, Object[]>();
1134: private List<DatabaseModificationListener> _listeners = new ArrayList<DatabaseModificationListener>();
1135: private String _name;
1136: private boolean _readOnly = false;
1137: private Map<String, Sequence> _sequences = new HashMap<String, Sequence>();
1138:
1139: private DatabaseModificationListener _colUpd = new AxionColumnsMetaTableUpdater(
1140: this );
1141: private DatabaseModificationListener _seqUpd = new AxionSequencesMetaTableUpdater(
1142: this );
1143:
1144: private Map<String, Table> _tables = new HashMap<String, Table>();
1145: private Map<String, TableFactory> _tableTypes = new HashMap<String, TableFactory>();
1146: private TransactionManager _transactionManager = new TransactionManagerImpl(
1147: this);
1148: }
|