Source Code Cross Referenced for Transaction.java in  » Database-DBMS » mckoi » com » mckoi » database » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database DBMS » mckoi » com.mckoi.database 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /**
0002:         * com.mckoi.database.Transaction  18 Nov 2000
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 com.mckoi.debug.*;
0026:        import com.mckoi.util.IntegerVector;
0027:        import com.mckoi.util.BigNumber;
0028:        import com.mckoi.database.global.ByteLongObject;
0029:        import com.mckoi.database.global.ObjectTranslator;
0030:        import java.io.IOException;
0031:        import java.util.ArrayList;
0032:        import java.util.HashMap;
0033:
0034:        /**
0035:         * An open transaction that manages all data access to the
0036:         * TableDataConglomerate.  A transaction sees a view of the data as it was when
0037:         * the transaction was created.  It also sees any modifications that were made
0038:         * within the context of this transaction.  It does not see modifications made
0039:         * by other open transactions.
0040:         * <p>
0041:         * A transaction ends when it is committed or rollbacked.  All operations
0042:         * on this transaction object only occur within the context of this transaction
0043:         * and are not permanent changes to the database structure.  Only when the
0044:         * transaction is committed are changes reflected in the master data.
0045:         *
0046:         * @author Tobias Downer
0047:         */
0048:
0049:        public class Transaction extends SimpleTransaction {
0050:
0051:            // ---------- Constraint statics ----------
0052:            // These statics are for managing constraints.
0053:
0054:            /**
0055:             * The type of deferrance.
0056:             */
0057:            public static final short INITIALLY_DEFERRED = java.sql.DatabaseMetaData.importedKeyInitiallyDeferred;
0058:            public static final short INITIALLY_IMMEDIATE = java.sql.DatabaseMetaData.importedKeyInitiallyImmediate;
0059:            public static final short NOT_DEFERRABLE = java.sql.DatabaseMetaData.importedKeyNotDeferrable;
0060:
0061:            /**
0062:             * Foreign key referential trigger actions.
0063:             */
0064:            public static final String NO_ACTION = "NO ACTION";
0065:            public static final String CASCADE = "CASCADE";
0066:            public static final String SET_NULL = "SET NULL";
0067:            public static final String SET_DEFAULT = "SET DEFAULT";
0068:
0069:            // ---------- Member variables ----------
0070:
0071:            /**
0072:             * The TableDataConglomerate that this transaction is within the context of.
0073:             */
0074:            private TableDataConglomerate conglomerate;
0075:
0076:            /**
0077:             * The commit_id that represents the id of the last commit that occurred
0078:             * when this transaction was created.
0079:             */
0080:            private long commit_id;
0081:
0082:            /**
0083:             * All tables touched by this transaction.  (MutableTableDataSource)
0084:             */
0085:            private ArrayList touched_tables;
0086:
0087:            /**
0088:             * All tables selected from in this transaction.  (MasterTableDataSource)
0089:             */
0090:            private ArrayList selected_from_tables;
0091:
0092:            /**
0093:             * The name of all database objects that were created in this transaction.
0094:             * This is used for a namespace collision test during commit.
0095:             */
0096:            private ArrayList created_database_objects;
0097:
0098:            /**
0099:             * The name of all database objects that were dropped in this transaction.
0100:             * This is used for a namespace collision test during commit.
0101:             */
0102:            private ArrayList dropped_database_objects;
0103:
0104:            /**
0105:             * The journal for this transaction.  This journal describes all changes
0106:             * made to the database by this transaction.
0107:             */
0108:            private TransactionJournal journal;
0109:
0110:            /**
0111:             * The list of InternalTableInfo objects that are containers for generating
0112:             * internal tables (GTDataSource).
0113:             */
0114:            private InternalTableInfo[] internal_tables;
0115:
0116:            /**
0117:             * A pointer in the internal_tables list.
0118:             */
0119:            private int internal_tables_i;
0120:
0121:            /**
0122:             * True if an error should be generated on a dirty select.
0123:             */
0124:            private boolean transaction_error_on_dirty_select;
0125:
0126:            /**
0127:             * True if this transaction is closed.
0128:             */
0129:            private boolean closed;
0130:
0131:            /**
0132:             * Constructs the transaction.
0133:             */
0134:            Transaction(TableDataConglomerate conglomerate, long commit_id,
0135:                    ArrayList visible_tables, ArrayList table_indices) {
0136:
0137:                super (conglomerate.getSystem(), conglomerate
0138:                        .getSequenceManager());
0139:
0140:                this .conglomerate = conglomerate;
0141:                this .commit_id = commit_id;
0142:                this .closed = false;
0143:
0144:                this .created_database_objects = new ArrayList();
0145:                this .dropped_database_objects = new ArrayList();
0146:
0147:                this .touched_tables = new ArrayList();
0148:                this .selected_from_tables = new ArrayList();
0149:                journal = new TransactionJournal();
0150:
0151:                // Set up all the visible tables
0152:                int sz = visible_tables.size();
0153:                for (int i = 0; i < sz; ++i) {
0154:                    addVisibleTable((MasterTableDataSource) visible_tables
0155:                            .get(i), (IndexSet) table_indices.get(i));
0156:                }
0157:
0158:                // NOTE: We currently only support 8 - internal tables to the transaction
0159:                //  layer, and internal tables to the database connection layer.
0160:                internal_tables = new InternalTableInfo[8];
0161:                internal_tables_i = 0;
0162:                addInternalTableInfo(new TransactionInternalTables());
0163:
0164:                getSystem().stats().increment("Transaction.count");
0165:
0166:                // Defaults to true (should be changed by called 'setErrorOnDirtySelect'
0167:                // method.
0168:                transaction_error_on_dirty_select = true;
0169:            }
0170:
0171:            /**
0172:             * Returns the TableDataConglomerate of this transaction.
0173:             */
0174:            final TableDataConglomerate getConglomerate() {
0175:                return conglomerate;
0176:            }
0177:
0178:            /**
0179:             * Adds an internal table container (InternalTableInfo) used to
0180:             * resolve internal tables.  This is intended as a way for the
0181:             * DatabaseConnection layer to plug in 'virtual' tables, such as those
0182:             * showing connection statistics, etc.  It also allows modelling database
0183:             * objects as tables, such as sequences, triggers, procedures, etc.
0184:             */
0185:            void addInternalTableInfo(InternalTableInfo info) {
0186:                if (internal_tables_i >= internal_tables.length) {
0187:                    throw new RuntimeException(
0188:                            "Internal table list bounds reached.");
0189:                }
0190:                internal_tables[internal_tables_i] = info;
0191:                ++internal_tables_i;
0192:            }
0193:
0194:            /**
0195:             * Returns the 'commit_id' which is the last commit that occured before
0196:             * this transaction was created.
0197:             * <p>
0198:             * NOTE: Don't make this synchronized over anything.  This is accessed
0199:             *   by OpenTransactionList.
0200:             */
0201:            long getCommitID() {
0202:                // REINFORCED NOTE: This absolutely must never be synchronized because
0203:                //   it is accessed by OpenTransactionList synchronized.
0204:                return commit_id;
0205:            }
0206:
0207:            // ----- Operations within the context of this transaction -----
0208:
0209:            /**
0210:             * Overwritten from SimpleTransaction.
0211:             * Returns a new MutableTableDataSource for the view of the
0212:             * MasterTableDataSource at the start of this transaction.  Note that this is
0213:             * only ever called once per table accessed in this transaction.
0214:             */
0215:            public MutableTableDataSource createMutableTableDataSourceAtCommit(
0216:                    MasterTableDataSource master) {
0217:                // Create the table for this transaction.
0218:                MutableTableDataSource table = master
0219:                        .createTableDataSourceAtCommit(this );
0220:                // Log in the journal that this table was touched by the transaction.
0221:                journal.entryAddTouchedTable(master.getTableID());
0222:                touched_tables.add(table);
0223:                return table;
0224:            }
0225:
0226:            /**
0227:             * Called by the query evaluation layer when information is selected
0228:             * from this table as part of this transaction.  When there is a select
0229:             * query on a table, when the transaction is committed we should look for
0230:             * any concurrently committed changes to the table.  If there are any, then
0231:             * any selects on the table should be considered incorrect and cause a
0232:             * commit failure.
0233:             */
0234:            public void addSelectedFromTable(TableName table_name) {
0235:                // Special handling of internal tables,
0236:                if (isDynamicTable(table_name)) {
0237:                    return;
0238:                }
0239:
0240:                MasterTableDataSource master = findVisibleTable(table_name,
0241:                        false);
0242:                if (master == null) {
0243:                    throw new StatementException(
0244:                            "Table with name not available: " + table_name);
0245:                }
0246:                //    System.out.println("Selected from table: " + table_name);
0247:                synchronized (selected_from_tables) {
0248:                    if (!selected_from_tables.contains(master)) {
0249:                        selected_from_tables.add(master);
0250:                    }
0251:                }
0252:
0253:            }
0254:
0255:            /**
0256:             * Copies all the tables within this transaction view to the destination
0257:             * conglomerate object.  Some care should be taken with security when using
0258:             * this method.  This is useful for generating a backup of the current
0259:             * view of the database that can work without interfering with the general
0260:             * operation of the database.
0261:             */
0262:            void liveCopyAllDataTo(TableDataConglomerate dest_conglomerate) {
0263:                // Create a new TableDataConglomerate using the same settings from this
0264:                // TransactionSystem but on the new StoreSystem.
0265:                int sz = getVisibleTableCount();
0266:
0267:                // The list to copy (in the order to copy in).
0268:                // We put the 'SEQUENCE_INFO' at the very end of the table list to copy.
0269:                ArrayList copy_list = new ArrayList(sz);
0270:
0271:                MasterTableDataSource last_entry = null;
0272:                for (int i = 0; i < sz; ++i) {
0273:                    MasterTableDataSource master_table = getVisibleTable(i);
0274:                    TableName table_name = master_table.getDataTableDef()
0275:                            .getTableName();
0276:                    if (table_name
0277:                            .equals(TableDataConglomerate.SYS_SEQUENCE_INFO)) {
0278:                        last_entry = master_table;
0279:                    } else {
0280:                        copy_list.add(master_table);
0281:                    }
0282:                }
0283:                copy_list.add(0, last_entry);
0284:
0285:                try {
0286:                    // For each master table,
0287:                    for (int i = 0; i < sz; ++i) {
0288:
0289:                        MasterTableDataSource master_table = (MasterTableDataSource) copy_list
0290:                                .get(i);
0291:                        TableName table_name = master_table.getDataTableDef()
0292:                                .getTableName();
0293:
0294:                        // Create a destination transaction
0295:                        Transaction dest_transaction = dest_conglomerate
0296:                                .createTransaction();
0297:
0298:                        // The view of this table within this transaction.
0299:                        IndexSet index_set = getIndexSetForTable(master_table);
0300:
0301:                        // If the table already exists then drop it
0302:                        if (dest_transaction.tableExists(table_name)) {
0303:                            dest_transaction.dropTable(table_name);
0304:                        }
0305:
0306:                        // Copy it into the destination conglomerate.
0307:                        dest_transaction.copyTable(master_table, index_set);
0308:
0309:                        // Close and commit the transaction in the destination conglomeration.      
0310:                        dest_transaction.closeAndCommit();
0311:
0312:                        // Dispose the IndexSet
0313:                        index_set.dispose();
0314:
0315:                    }
0316:
0317:                } catch (TransactionException e) {
0318:                    Debug().writeException(e);
0319:                    throw new RuntimeException(
0320:                            "Transaction Error when copying table: "
0321:                                    + e.getMessage());
0322:                }
0323:            }
0324:
0325:            // ---------- Dynamically generated tables ----------
0326:
0327:            /**
0328:             * Returns true if the given table name represents a dynamically generated
0329:             * system table.
0330:             */
0331:            protected boolean isDynamicTable(TableName table_name) {
0332:                for (int i = 0; i < internal_tables.length; ++i) {
0333:                    InternalTableInfo info = internal_tables[i];
0334:                    if (info != null) {
0335:                        if (info.containsTableName(table_name)) {
0336:                            return true;
0337:                        }
0338:                    }
0339:                }
0340:                return false;
0341:            }
0342:
0343:            /**
0344:             * Returns a list of all dynamic table names.  This method returns a
0345:             * reference to a static, make sure you don't change the contents of the
0346:             * array!
0347:             */
0348:            protected TableName[] getDynamicTableList() {
0349:                int sz = 0;
0350:                for (int i = 0; i < internal_tables.length; ++i) {
0351:                    InternalTableInfo info = internal_tables[i];
0352:                    if (info != null) {
0353:                        sz += info.getTableCount();
0354:                    }
0355:                }
0356:
0357:                TableName[] list = new TableName[sz];
0358:                int index = 0;
0359:
0360:                for (int i = 0; i < internal_tables.length; ++i) {
0361:                    InternalTableInfo info = internal_tables[i];
0362:                    if (info != null) {
0363:                        sz = info.getTableCount();
0364:                        for (int n = 0; n < sz; ++n) {
0365:                            list[index] = info.getTableName(n);
0366:                            ++index;
0367:                        }
0368:                    }
0369:                }
0370:
0371:                return list;
0372:            }
0373:
0374:            /**
0375:             * Returns the DataTableDef for the given internal table.
0376:             */
0377:            protected DataTableDef getDynamicDataTableDef(TableName table_name) {
0378:
0379:                for (int i = 0; i < internal_tables.length; ++i) {
0380:                    InternalTableInfo info = internal_tables[i];
0381:                    if (info != null) {
0382:                        int index = info.findTableName(table_name);
0383:                        if (index != -1) {
0384:                            return info.getDataTableDef(index);
0385:                        }
0386:                    }
0387:                }
0388:
0389:                throw new RuntimeException("Not an internal table: "
0390:                        + table_name);
0391:            }
0392:
0393:            /**
0394:             * Returns an instance of MutableDataTableSource that represents the
0395:             * contents of the internal table with the given name.
0396:             */
0397:            protected MutableTableDataSource getDynamicTable(
0398:                    TableName table_name) {
0399:
0400:                for (int i = 0; i < internal_tables.length; ++i) {
0401:                    InternalTableInfo info = internal_tables[i];
0402:                    if (info != null) {
0403:                        int index = info.findTableName(table_name);
0404:                        if (index != -1) {
0405:                            return info.createInternalTable(index);
0406:                        }
0407:                    }
0408:                }
0409:
0410:                throw new RuntimeException("Not an internal table: "
0411:                        + table_name);
0412:            }
0413:
0414:            /**
0415:             * Returns a string type describing the type of the dynamic table.
0416:             */
0417:            public String getDynamicTableType(TableName table_name) {
0418:                // Otherwise we need to look up the table in the internal table list,
0419:                for (int i = 0; i < internal_tables.length; ++i) {
0420:                    InternalTableInfo info = internal_tables[i];
0421:                    if (info != null) {
0422:                        int index = info.findTableName(table_name);
0423:                        if (index != -1) {
0424:                            return info.getTableType(index);
0425:                        }
0426:                    }
0427:                }
0428:                // No internal table found, so report the error.
0429:                throw new RuntimeException("No table '" + table_name
0430:                        + "' to report type for.");
0431:            }
0432:
0433:            // ---------- Transaction manipulation ----------
0434:
0435:            /**
0436:             * Creates a new table within this transaction with the given sector size.
0437:             * If the table already exists then an exception is thrown.
0438:             * <p>
0439:             * This should only be called under an exclusive lock on the connection.
0440:             */
0441:            public void createTable(DataTableDef table_def,
0442:                    int data_sector_size, int index_sector_size) {
0443:
0444:                TableName table_name = table_def.getTableName();
0445:                MasterTableDataSource master = findVisibleTable(table_name,
0446:                        false);
0447:                if (master != null) {
0448:                    throw new StatementException("Table '" + table_name
0449:                            + "' already exists.");
0450:                }
0451:
0452:                table_def.setImmutable();
0453:
0454:                if (data_sector_size < 27) {
0455:                    data_sector_size = 27;
0456:                } else if (data_sector_size > 4096) {
0457:                    data_sector_size = 4096;
0458:                }
0459:
0460:                // Create the new master table and add to list of visible tables.
0461:                master = conglomerate.createMasterTable(table_def,
0462:                        data_sector_size, index_sector_size);
0463:                // Add this table (and an index set) for this table.
0464:                addVisibleTable(master, master.createIndexSet());
0465:
0466:                // Log in the journal that this transaction touched the table_id.
0467:                int table_id = master.getTableID();
0468:                journal.entryAddTouchedTable(table_id);
0469:
0470:                // Log in the journal that we created this table.
0471:                journal.entryTableCreate(table_id);
0472:
0473:                // Add entry to the Sequences table for the native generator for this
0474:                // table.
0475:                SequenceManager.addNativeTableGenerator(this , table_name);
0476:
0477:                // Notify that this database object has been successfully created.
0478:                databaseObjectCreated(table_name);
0479:
0480:            }
0481:
0482:            /**
0483:             * Creates a new table within this transaction.  If the table already
0484:             * exists then an exception is thrown.
0485:             * <p>
0486:             * This should only be called under an exclusive lock on the connection.
0487:             */
0488:            public void createTable(DataTableDef table_def) {
0489:                // data sector size defaults to 251
0490:                // index sector size defaults to 1024
0491:                createTable(table_def, 251, 1024);
0492:            }
0493:
0494:            /**
0495:             * Given a DataTableDef, if the table exists then it is updated otherwise
0496:             * if it doesn't exist then it is created.
0497:             * <p>
0498:             * This should only be used as very fine grain optimization for creating/
0499:             * altering tables.  If in the future the underlying table model is changed
0500:             * so that the given 'sector_size' value is unapplicable, then the value
0501:             * will be ignored.
0502:             */
0503:            public void alterCreateTable(DataTableDef table_def,
0504:                    int data_sector_size, int index_sector_size) {
0505:                if (!tableExists(table_def.getTableName())) {
0506:                    createTable(table_def, data_sector_size, index_sector_size);
0507:                } else {
0508:                    alterTable(table_def.getTableName(), table_def,
0509:                            data_sector_size, index_sector_size);
0510:                }
0511:            }
0512:
0513:            /**
0514:             * Drops a table within this transaction.  If the table does not exist then
0515:             * an exception is thrown.
0516:             * <p>
0517:             * This should only be called under an exclusive lock on the connection.
0518:             */
0519:            public void dropTable(TableName table_name) {
0520:                //    System.out.println(this + " DROP: " + table_name);
0521:                MasterTableDataSource master = findVisibleTable(table_name,
0522:                        false);
0523:
0524:                if (master == null) {
0525:                    throw new StatementException("Table '" + table_name
0526:                            + "' doesn't exist.");
0527:                }
0528:
0529:                // Removes this table from the visible table list of this transaction
0530:                removeVisibleTable(master);
0531:
0532:                // Log in the journal that this transaction touched the table_id.
0533:                int table_id = master.getTableID();
0534:                journal.entryAddTouchedTable(table_id);
0535:
0536:                // Log in the journal that we dropped this table.
0537:                journal.entryTableDrop(table_id);
0538:
0539:                // Remove the native sequence generator (in this transaction) for this
0540:                // table.
0541:                SequenceManager.removeNativeTableGenerator(this , table_name);
0542:
0543:                // Notify that this database object has been dropped
0544:                databaseObjectDropped(table_name);
0545:
0546:            }
0547:
0548:            /**
0549:             * Generates an exact copy of the table within this transaction.  It is
0550:             * recommended that the table is dropped before the copy is made.  The
0551:             * purpose of this method is to generate a temporary table that can be
0552:             * modified without fear of another transaction changing the contents in
0553:             * another transaction.  This also provides a convenient way to compact
0554:             * a table because any spare space is removed when the table is copied.  It
0555:             * also allows us to make a copy of MasterTableDataSource into a foreign
0556:             * conglomerate which allows us to implement a backup procedure.
0557:             * <p>
0558:             * This method does NOT assume the given MasterTableDataSource is contained,
0559:             * or has once been contained within this conglomerate.
0560:             */
0561:            public void copyTable(MasterTableDataSource src_master_table,
0562:                    IndexSet index_set) {
0563:
0564:                DataTableDef table_def = src_master_table.getDataTableDef();
0565:                TableName table_name = table_def.getTableName();
0566:                MasterTableDataSource master = findVisibleTable(table_name,
0567:                        false);
0568:                if (master != null) {
0569:                    throw new StatementException("Unable to copy.  Table '"
0570:                            + table_name + "' already exists.");
0571:                }
0572:
0573:                // Copy the master table and add to the list of visible tables.
0574:                master = conglomerate.copyMasterTable(src_master_table,
0575:                        index_set);
0576:                // Add this visible table
0577:                addVisibleTable(master, master.createIndexSet());
0578:
0579:                // Log in the journal that this transaction touched the table_id.
0580:                int table_id = master.getTableID();
0581:                journal.entryAddTouchedTable(table_id);
0582:
0583:                // Log in the journal that we created this table.
0584:                journal.entryTableCreate(table_id);
0585:
0586:                // Add entry to the Sequences table for the native generator for this
0587:                // table.
0588:                SequenceManager.addNativeTableGenerator(this , table_name);
0589:
0590:                // Notify that this database object has been successfully created.
0591:                databaseObjectCreated(table_name);
0592:
0593:            }
0594:
0595:            /**
0596:             * Alter the table with the given name to the new definition and give the
0597:             * copied table a new data sector size.  If the table does not exist then
0598:             * an exception is thrown.
0599:             * <p>
0600:             * This copies all columns that were in the original table to the new
0601:             * altered table if the name is the same.  Any names that don't exist are
0602:             * set to the default value.
0603:             * <p>
0604:             * This should only be called under an exclusive lock on the connection.
0605:             */
0606:            public void alterTable(TableName table_name,
0607:                    DataTableDef table_def, int data_sector_size,
0608:                    int index_sector_size) {
0609:
0610:                table_def.setImmutable();
0611:
0612:                // The current schema context is the schema of the table name
0613:                String current_schema = table_name.getSchema();
0614:                SystemQueryContext context = new SystemQueryContext(this ,
0615:                        current_schema);
0616:
0617:                // Get the next unique id of the unaltered table.
0618:                long next_id = nextUniqueID(table_name);
0619:
0620:                // Drop the current table
0621:                MutableTableDataSource c_table = getTable(table_name);
0622:                dropTable(table_name);
0623:                // And create the table table
0624:                createTable(table_def);
0625:                MutableTableDataSource altered_table = getTable(table_name);
0626:
0627:                // Get the new MasterTableDataSource object
0628:                MasterTableDataSource new_master_table = findVisibleTable(
0629:                        table_name, false);
0630:                // Set the sequence id of the table
0631:                new_master_table.setUniqueID(next_id);
0632:
0633:                // Work out which columns we have to copy to where
0634:                int[] col_map = new int[table_def.columnCount()];
0635:                DataTableDef orig_td = c_table.getDataTableDef();
0636:                for (int i = 0; i < col_map.length; ++i) {
0637:                    String col_name = table_def.columnAt(i).getName();
0638:                    col_map[i] = orig_td.findColumnName(col_name);
0639:                }
0640:
0641:                try {
0642:                    // First move all the rows from the old table to the new table,
0643:                    // This does NOT update the indexes.
0644:                    try {
0645:                        RowEnumeration e = c_table.rowEnumeration();
0646:                        while (e.hasMoreRows()) {
0647:                            int row_index = e.nextRowIndex();
0648:                            RowData row_data = new RowData(altered_table);
0649:                            for (int i = 0; i < col_map.length; ++i) {
0650:                                int col = col_map[i];
0651:                                if (col != -1) {
0652:                                    row_data.setColumnData(i, c_table
0653:                                            .getCellContents(col, row_index));
0654:                                }
0655:                            }
0656:                            row_data.setDefaultForRest(context);
0657:                            // Note we use a low level 'addRow' method on the master table
0658:                            // here.  This does not touch the table indexes.  The indexes are
0659:                            // built later.
0660:                            int new_row_number = new_master_table
0661:                                    .addRow(row_data);
0662:                            // Set the record as committed added
0663:                            new_master_table.writeRecordType(new_row_number,
0664:                                    0x010);
0665:                        }
0666:                    } catch (DatabaseException e) {
0667:                        Debug().writeException(e);
0668:                        throw new RuntimeException(e.getMessage());
0669:                    }
0670:
0671:                    // PENDING: We need to copy any existing index definitions that might
0672:                    //   have been set on the table being altered.
0673:
0674:                    // Rebuild the indexes in the new master table,
0675:                    new_master_table.buildIndexes();
0676:
0677:                    // Get the snapshot index set on the new table and set it here
0678:                    setIndexSetForTable(new_master_table, new_master_table
0679:                            .createIndexSet());
0680:
0681:                    // Flush this out of the table cache
0682:                    flushTableCache(table_name);
0683:
0684:                    // Ensure the native sequence generator exists...
0685:                    SequenceManager
0686:                            .removeNativeTableGenerator(this , table_name);
0687:                    SequenceManager.addNativeTableGenerator(this , table_name);
0688:
0689:                    // Notify that this database object has been successfully dropped and
0690:                    // created.
0691:                    databaseObjectDropped(table_name);
0692:                    databaseObjectCreated(table_name);
0693:
0694:                } catch (IOException e) {
0695:                    Debug().writeException(e);
0696:                    throw new RuntimeException(e.getMessage());
0697:                }
0698:
0699:            }
0700:
0701:            /**
0702:             * Alters the table with the given name within this transaction to the
0703:             * specified table definition.  If the table does not exist then an exception
0704:             * is thrown.
0705:             * <p>
0706:             * This should only be called under an exclusive lock on the connection.
0707:             */
0708:            public void alterTable(TableName table_name, DataTableDef table_def) {
0709:
0710:                // Make sure we remember the current sector size of the altered table so
0711:                // we can create the new table with the original size.
0712:                try {
0713:
0714:                    int current_data_sector_size;
0715:                    MasterTableDataSource master = findVisibleTable(table_name,
0716:                            false);
0717:                    if (master instanceof  V1MasterTableDataSource) {
0718:                        current_data_sector_size = ((V1MasterTableDataSource) master)
0719:                                .rawDataSectorSize();
0720:                    } else {
0721:                        current_data_sector_size = -1;
0722:                    }
0723:                    // HACK: We use index sector size of 2043 for all altered tables
0724:                    alterTable(table_name, table_def, current_data_sector_size,
0725:                            2043);
0726:
0727:                } catch (IOException e) {
0728:                    throw new RuntimeException("IO Error: " + e.getMessage());
0729:                }
0730:
0731:            }
0732:
0733:            /**
0734:             * Checks all the rows in the table for immediate constraint violations
0735:             * and when the transaction is next committed check for all deferred
0736:             * constraint violations.  This method is used when the constraints on a
0737:             * table changes and we need to determine if any constraint violations
0738:             * occurred.  To the constraint checking system, this is like adding all
0739:             * the rows to the given table.
0740:             */
0741:            public void checkAllConstraints(TableName table_name) {
0742:                // Get the table
0743:                TableDataSource table = getTable(table_name);
0744:                // Get all the rows in the table
0745:                int[] rows = new int[table.getRowCount()];
0746:                RowEnumeration row_enum = table.rowEnumeration();
0747:                int i = 0;
0748:                while (row_enum.hasMoreRows()) {
0749:                    rows[i] = row_enum.nextRowIndex();
0750:                    ++i;
0751:                }
0752:                // Check the constraints of all the rows in the table.
0753:                TableDataConglomerate.checkAddConstraintViolations(this , table,
0754:                        rows, INITIALLY_IMMEDIATE);
0755:
0756:                // Add that we altered this table in the journal
0757:                MasterTableDataSource master = findVisibleTable(table_name,
0758:                        false);
0759:                if (master == null) {
0760:                    throw new StatementException("Table '" + table_name
0761:                            + "' doesn't exist.");
0762:                }
0763:
0764:                // Log in the journal that this transaction touched the table_id.
0765:                int table_id = master.getTableID();
0766:
0767:                journal.entryAddTouchedTable(table_id);
0768:                // Log in the journal that we dropped this table.
0769:                journal.entryTableConstraintAlter(table_id);
0770:
0771:            }
0772:
0773:            /**
0774:             * Compacts the table with the given name within this transaction.  If the
0775:             * table doesn't exist then an exception is thrown.
0776:             */
0777:            public void compactTable(TableName table_name) {
0778:
0779:                // Find the master table.
0780:                MasterTableDataSource current_table = findVisibleTable(
0781:                        table_name, false);
0782:                if (current_table == null) {
0783:                    throw new StatementException("Table '" + table_name
0784:                            + "' doesn't exist.");
0785:                }
0786:
0787:                // If the table is worth compacting, or the table is a
0788:                // V1MasterTableDataSource
0789:                if (current_table.isWorthCompacting()) {
0790:                    // The view of this table within this transaction.
0791:                    IndexSet index_set = getIndexSetForTable(current_table);
0792:                    // Drop the current table
0793:                    dropTable(table_name);
0794:                    // And copy to the new table
0795:                    copyTable(current_table, index_set);
0796:                }
0797:
0798:            }
0799:
0800:            /**
0801:             * Returns true if the conglomerate commit procedure should check for
0802:             * dirty selects and produce a transaction error.  A dirty select is when
0803:             * a query reads information from a table that is effected by another table
0804:             * during a transaction.  This in itself will not cause data
0805:             * consistancy problems but for strict conformance to SERIALIZABLE
0806:             * isolation level this should return true.
0807:             * <p>
0808:             * NOTE; We MUST NOT make this method serialized because it is back called
0809:             *   from within a commit lock in TableDataConglomerate.
0810:             */
0811:            boolean transactionErrorOnDirtySelect() {
0812:                return transaction_error_on_dirty_select;
0813:            }
0814:
0815:            /**
0816:             * Sets the transaction error on dirty select for this transaction.
0817:             */
0818:            void setErrorOnDirtySelect(boolean status) {
0819:                transaction_error_on_dirty_select = status;
0820:            }
0821:
0822:            // ----- Setting/Querying constraint information -----
0823:            // PENDING: Is it worth implementing a pluggable constraint architecture
0824:            //   as described in the idea below.  With the current implementation we
0825:            //   have tied a DataTableConglomerate to a specific constraint
0826:            //   architecture.
0827:            //
0828:            // IDEA: These methods delegate to the parent conglomerate which has a
0829:            //   pluggable architecture for setting/querying constraints.  Some uses of
0830:            //   a conglomerate may not need integrity constraints or may implement the
0831:            //   mechanism for storing/querying in a different way.  This provides a
0832:            //   useful abstraction of being enable to implement constraint behaviour
0833:            //   by only providing a way to set/query the constraint information in
0834:            //   different conglomerate uses.
0835:
0836:            /**
0837:             * Convenience, given a SimpleTableQuery object this will return a list of
0838:             * column names in sequence that represent the columns in a group constraint.
0839:             * <p>
0840:             * 'cols' is the unsorted list of indexes in the table that represent the
0841:             * group.
0842:             * <p>
0843:             * Assumes column 2 of dt is the sequence number and column 1 is the name
0844:             * of the column.
0845:             */
0846:            private static String[] toColumns(SimpleTableQuery dt,
0847:                    IntegerVector cols) {
0848:                int size = cols.size();
0849:                String[] list = new String[size];
0850:
0851:                // for each n of the output list
0852:                for (int n = 0; n < size; ++n) {
0853:                    // for each i of the input list
0854:                    for (int i = 0; i < size; ++i) {
0855:                        int row_index = cols.intAt(i);
0856:                        int seq_no = ((BigNumber) dt.get(2, row_index)
0857:                                .getObject()).intValue();
0858:                        if (seq_no == n) {
0859:                            list[n] = dt.get(1, row_index).getObject()
0860:                                    .toString();
0861:                            break;
0862:                        }
0863:                    }
0864:                }
0865:
0866:                return list;
0867:            }
0868:
0869:            /**
0870:             * Convenience, generates a unique constraint name.  If the given constraint
0871:             * name is 'null' then a new one is created, otherwise the given default
0872:             * one is returned.
0873:             */
0874:            private static String makeUniqueConstraintName(String name,
0875:                    BigNumber unique_id) {
0876:                if (name == null) {
0877:                    name = "_ANONYMOUS_CONSTRAINT_" + unique_id.toString();
0878:                }
0879:                return name;
0880:            }
0881:
0882:            /**
0883:             * Notifies this transaction that a database object with the given name has
0884:             * successfully been created.
0885:             */
0886:            void databaseObjectCreated(TableName table_name) {
0887:                // If this table name was dropped, then remove from the drop list
0888:                boolean dropped = dropped_database_objects.remove(table_name);
0889:                // If the above operation didn't remove a table name then add to the
0890:                // created database objects list.
0891:                if (!dropped) {
0892:                    created_database_objects.add(table_name);
0893:                }
0894:            }
0895:
0896:            /**
0897:             * Notifies this transaction that a database object with the given name has
0898:             * successfully been dropped.
0899:             */
0900:            void databaseObjectDropped(TableName table_name) {
0901:                // If this table name was created, then remove from the create list
0902:                boolean created = created_database_objects.remove(table_name);
0903:                // If the above operation didn't remove a table name then add to the
0904:                // dropped database objects list.
0905:                if (!created) {
0906:                    dropped_database_objects.add(table_name);
0907:                }
0908:            }
0909:
0910:            /**
0911:             * Returns the normalized list of database object names created in this
0912:             * transaction.
0913:             */
0914:            ArrayList getAllNamesCreated() {
0915:                return created_database_objects;
0916:            }
0917:
0918:            /**
0919:             * Returns the normalized list of database object names dropped in this
0920:             * transaction.
0921:             */
0922:            ArrayList getAllNamesDropped() {
0923:                return dropped_database_objects;
0924:            }
0925:
0926:            /**
0927:             * Create a new schema in this transaction.  When the transaction is
0928:             * committed the schema will become globally accessable.  Note that any
0929:             * security checks must be performed before this method is called.
0930:             * <p>
0931:             * NOTE: We must guarentee that the transaction be in exclusive mode before
0932:             *   this method is called.
0933:             */
0934:            public void createSchema(String name, String type) {
0935:                TableName table_name = TableDataConglomerate.SCHEMA_INFO_TABLE;
0936:                MutableTableDataSource t = getTable(table_name);
0937:                SimpleTableQuery dt = new SimpleTableQuery(t);
0938:
0939:                try {
0940:                    // Select entries where;
0941:                    //     sUSRSchemaInfo.name = name
0942:                    if (!dt.existsSingle(1, name)) {
0943:                        // Add the entry to the schema info table.
0944:                        RowData rd = new RowData(t);
0945:                        BigNumber unique_id = BigNumber
0946:                                .fromLong(nextUniqueID(table_name));
0947:                        rd.setColumnDataFromObject(0, unique_id);
0948:                        rd.setColumnDataFromObject(1, name);
0949:                        rd.setColumnDataFromObject(2, type);
0950:                        // Third (other) column is left as null
0951:                        t.addRow(rd);
0952:                    } else {
0953:                        throw new StatementException("Schema already exists: "
0954:                                + name);
0955:                    }
0956:                } finally {
0957:                    dt.dispose();
0958:                }
0959:            }
0960:
0961:            /**
0962:             * Drops a schema from this transaction.  When the transaction is committed
0963:             * the schema will be dropped perminently.  Note that any security checks
0964:             * must be performed before this method is called.
0965:             * <p>
0966:             * NOTE: We must guarentee that the transaction be in exclusive mode before
0967:             *   this method is called.
0968:             */
0969:            public void dropSchema(String name) {
0970:                TableName table_name = TableDataConglomerate.SCHEMA_INFO_TABLE;
0971:                MutableTableDataSource t = getTable(table_name);
0972:                SimpleTableQuery dt = new SimpleTableQuery(t);
0973:
0974:                // Drop a single entry from dt where column 1 = name
0975:                boolean b = dt.deleteSingle(1, name);
0976:                dt.dispose();
0977:                if (!b) {
0978:                    throw new StatementException("Schema doesn't exists: "
0979:                            + name);
0980:                }
0981:            }
0982:
0983:            /**
0984:             * Returns true if the schema exists within this transaction.
0985:             */
0986:            public boolean schemaExists(String name) {
0987:                TableName table_name = TableDataConglomerate.SCHEMA_INFO_TABLE;
0988:                MutableTableDataSource t = getTable(table_name);
0989:                SimpleTableQuery dt = new SimpleTableQuery(t);
0990:
0991:                // Returns true if there's a single entry in dt where column 1 = name
0992:                boolean b = dt.existsSingle(1, name);
0993:                dt.dispose();
0994:                return b;
0995:            }
0996:
0997:            /**
0998:             * Resolves the case of the given schema name if the database is performing
0999:             * case insensitive identifier matching.  Returns a SchemaDef object that
1000:             * identifiers the schema.  Returns null if the schema name could not be
1001:             * resolved.
1002:             */
1003:            public SchemaDef resolveSchemaCase(String name, boolean ignore_case) {
1004:                // The list of schema
1005:                SimpleTableQuery dt = new SimpleTableQuery(
1006:                        getTable(TableDataConglomerate.SCHEMA_INFO_TABLE));
1007:
1008:                try {
1009:                    RowEnumeration e = dt.rowEnumeration();
1010:                    if (ignore_case) {
1011:                        SchemaDef result = null;
1012:                        while (e.hasMoreRows()) {
1013:                            int row_index = e.nextRowIndex();
1014:                            String cur_name = dt.get(1, row_index).getObject()
1015:                                    .toString();
1016:                            if (name.equalsIgnoreCase(cur_name)) {
1017:                                if (result != null) {
1018:                                    throw new StatementException(
1019:                                            "Ambiguous schema name: '" + name
1020:                                                    + "'");
1021:                                }
1022:                                String type = dt.get(2, row_index).getObject()
1023:                                        .toString();
1024:                                result = new SchemaDef(cur_name, type);
1025:                            }
1026:                        }
1027:                        return result;
1028:
1029:                    } else { // if (!ignore_case)
1030:                        while (e.hasMoreRows()) {
1031:                            int row_index = e.nextRowIndex();
1032:                            String cur_name = dt.get(1, row_index).getObject()
1033:                                    .toString();
1034:                            if (name.equals(cur_name)) {
1035:                                String type = dt.get(2, row_index).getObject()
1036:                                        .toString();
1037:                                return new SchemaDef(cur_name, type);
1038:                            }
1039:                        }
1040:                        // Not found
1041:                        return null;
1042:                    }
1043:                }
1044:
1045:                finally {
1046:                    dt.dispose();
1047:                }
1048:
1049:            }
1050:
1051:            /**
1052:             * Returns an array of SchemaDef objects for each schema currently setup in
1053:             * the database.
1054:             */
1055:            public SchemaDef[] getSchemaList() {
1056:                // The list of schema
1057:                SimpleTableQuery dt = new SimpleTableQuery(
1058:                        getTable(TableDataConglomerate.SCHEMA_INFO_TABLE));
1059:                RowEnumeration e = dt.rowEnumeration();
1060:                SchemaDef[] arr = new SchemaDef[dt.getRowCount()];
1061:                int i = 0;
1062:
1063:                while (e.hasMoreRows()) {
1064:                    int row_index = e.nextRowIndex();
1065:                    String cur_name = dt.get(1, row_index).getObject()
1066:                            .toString();
1067:                    String cur_type = dt.get(2, row_index).getObject()
1068:                            .toString();
1069:                    arr[i] = new SchemaDef(cur_name, cur_type);
1070:                    ++i;
1071:                }
1072:
1073:                dt.dispose();
1074:                return arr;
1075:            }
1076:
1077:            /**
1078:             * Sets a persistent variable of the database that becomes a committed
1079:             * change once this transaction is committed.  The variable can later be
1080:             * retrieved with a call to the 'getPersistantVar' method.  A persistant
1081:             * var is created if it doesn't exist in the DatabaseVars table otherwise
1082:             * it is overwritten.
1083:             */
1084:            public void setPersistentVar(String variable, String value) {
1085:                TableName table_name = TableDataConglomerate.PERSISTENT_VAR_TABLE;
1086:                MutableTableDataSource t = getTable(table_name);
1087:                SimpleTableQuery dt = new SimpleTableQuery(t);
1088:                dt.setVar(0, new Object[] { variable, value });
1089:                dt.dispose();
1090:            }
1091:
1092:            /**
1093:             * Returns the value of the persistent variable with the given name or null
1094:             * if it doesn't exist.
1095:             */
1096:            public String getPersistantVar(String variable) {
1097:                TableName table_name = TableDataConglomerate.PERSISTENT_VAR_TABLE;
1098:                MutableTableDataSource t = getTable(table_name);
1099:                SimpleTableQuery dt = new SimpleTableQuery(t);
1100:                String val = dt.getVar(1, 0, variable).toString();
1101:                dt.dispose();
1102:                return val;
1103:            }
1104:
1105:            /**
1106:             * Creates a new sequence generator with the given TableName and
1107:             * initializes it with the given details.  This does NOT check if the
1108:             * given name clashes with an existing database object.
1109:             */
1110:            public void createSequenceGenerator(TableName name,
1111:                    long start_value, long increment_by, long min_value,
1112:                    long max_value, long cache, boolean cycle) {
1113:                SequenceManager.createSequenceGenerator(this , name,
1114:                        start_value, increment_by, min_value, max_value, cache,
1115:                        cycle);
1116:
1117:                // Notify that this database object has been created
1118:                databaseObjectCreated(name);
1119:            }
1120:
1121:            /**
1122:             * Drops an existing sequence generator with the given name.
1123:             */
1124:            public void dropSequenceGenerator(TableName name) {
1125:                SequenceManager.dropSequenceGenerator(this , name);
1126:                // Flush the sequence manager
1127:                flushSequenceManager(name);
1128:
1129:                // Notify that this database object has been dropped
1130:                databaseObjectDropped(name);
1131:            }
1132:
1133:            /**
1134:             * Adds a unique constraint to the database which becomes perminant when
1135:             * the transaction is committed.  Columns in a table that are defined as
1136:             * unique are prevented from being duplicated by the engine.
1137:             * <p>
1138:             * NOTE: Security checks for adding constraints must be checked for at a
1139:             *   higher layer.
1140:             * <p>
1141:             * NOTE: We must guarentee that the transaction be in exclusive mode before
1142:             *   this method is called.
1143:             */
1144:            public void addUniqueConstraint(TableName table_name,
1145:                    String[] cols, short deferred, String constraint_name) {
1146:
1147:                TableName tn1 = TableDataConglomerate.UNIQUE_INFO_TABLE;
1148:                TableName tn2 = TableDataConglomerate.UNIQUE_COLS_TABLE;
1149:                MutableTableDataSource t = getTable(tn1);
1150:                MutableTableDataSource tcols = getTable(tn2);
1151:
1152:                try {
1153:
1154:                    // Insert a value into UNIQUE_INFO_TABLE
1155:                    RowData rd = new RowData(t);
1156:                    BigNumber unique_id = BigNumber.fromLong(nextUniqueID(tn1));
1157:                    constraint_name = makeUniqueConstraintName(constraint_name,
1158:                            unique_id);
1159:                    rd.setColumnDataFromObject(0, unique_id);
1160:                    rd.setColumnDataFromObject(1, constraint_name);
1161:                    rd.setColumnDataFromObject(2, table_name.getSchema());
1162:                    rd.setColumnDataFromObject(3, table_name.getName());
1163:                    rd.setColumnDataFromObject(4, BigNumber.fromInt(deferred));
1164:                    t.addRow(rd);
1165:
1166:                    // Insert the columns
1167:                    for (int i = 0; i < cols.length; ++i) {
1168:                        rd = new RowData(tcols);
1169:                        rd.setColumnDataFromObject(0, unique_id); // unique id
1170:                        rd.setColumnDataFromObject(1, cols[i]); // column name
1171:                        rd.setColumnDataFromObject(2, BigNumber.fromInt(i)); // sequence number
1172:                        tcols.addRow(rd);
1173:                    }
1174:
1175:                } catch (DatabaseConstraintViolationException e) {
1176:                    // Constraint violation when inserting the data.  Check the type and
1177:                    // wrap around an appropriate error message.
1178:                    if (e.getErrorCode() == DatabaseConstraintViolationException.UNIQUE_VIOLATION) {
1179:                        // This means we gave a constraint name that's already being used
1180:                        // for a primary key.
1181:                        throw new StatementException("Unique constraint name '"
1182:                                + constraint_name + "' is already being used.");
1183:                    }
1184:                    throw e;
1185:                }
1186:
1187:            }
1188:
1189:            /**
1190:             * Adds a foreign key constraint to the database which becomes perminent
1191:             * when the transaction is committed.  A foreign key represents a referential
1192:             * link from one table to another (may be the same table).  The 'table_name',
1193:             * 'cols' args represents the object to link from.  The 'ref_table',
1194:             * 'ref_cols' args represents the object to link to.  The update rules are
1195:             * for specifying cascading delete/update rules.  The deferred arg is for
1196:             * IMMEDIATE/DEFERRED checking.
1197:             * <p>
1198:             * NOTE: Security checks for adding constraints must be checked for at a
1199:             *   higher layer.
1200:             * <p>
1201:             * NOTE: We must guarentee that the transaction be in exclusive mode before
1202:             *   this method is called.
1203:             */
1204:            public void addForeignKeyConstraint(TableName table, String[] cols,
1205:                    TableName ref_table, String[] ref_cols, String delete_rule,
1206:                    String update_rule, short deferred, String constraint_name) {
1207:                TableName tn1 = TableDataConglomerate.FOREIGN_INFO_TABLE;
1208:                TableName tn2 = TableDataConglomerate.FOREIGN_COLS_TABLE;
1209:                MutableTableDataSource t = getTable(tn1);
1210:                MutableTableDataSource tcols = getTable(tn2);
1211:
1212:                try {
1213:
1214:                    // If 'ref_columns' empty then set to primary key for referenced table,
1215:                    // ISSUE: What if primary key changes after the fact?
1216:                    if (ref_cols.length == 0) {
1217:                        ColumnGroup set = queryTablePrimaryKeyGroup(this ,
1218:                                ref_table);
1219:                        if (set == null) {
1220:                            throw new StatementException(
1221:                                    "No primary key defined for referenced table '"
1222:                                            + ref_table + "'");
1223:                        }
1224:                        ref_cols = set.columns;
1225:                    }
1226:
1227:                    if (cols.length != ref_cols.length) {
1228:                        throw new StatementException("Foreign key reference '"
1229:                                + table + "' -> '" + ref_table
1230:                                + "' does not have an equal number of "
1231:                                + "column terms.");
1232:                    }
1233:
1234:                    // If delete or update rule is 'SET NULL' then check the foreign key
1235:                    // columns are not constrained as 'NOT NULL'
1236:                    if (delete_rule.equals("SET NULL")
1237:                            || update_rule.equals("SET NULL")) {
1238:                        DataTableDef table_def = getDataTableDef(table);
1239:                        for (int i = 0; i < cols.length; ++i) {
1240:                            DataTableColumnDef column_def = table_def
1241:                                    .columnAt(table_def.findColumnName(cols[i]));
1242:                            if (column_def.isNotNull()) {
1243:                                throw new StatementException(
1244:                                        "Foreign key reference '"
1245:                                                + table
1246:                                                + "' -> '"
1247:                                                + ref_table
1248:                                                + "' update or delete triggered "
1249:                                                + "action is SET NULL for columns that are constrained as "
1250:                                                + "NOT NULL.");
1251:                            }
1252:                        }
1253:                    }
1254:
1255:                    // Insert a value into FOREIGN_INFO_TABLE
1256:                    RowData rd = new RowData(t);
1257:                    BigNumber unique_id = BigNumber.fromLong(nextUniqueID(tn1));
1258:                    constraint_name = makeUniqueConstraintName(constraint_name,
1259:                            unique_id);
1260:                    rd.setColumnDataFromObject(0, unique_id);
1261:                    rd.setColumnDataFromObject(1, constraint_name);
1262:                    rd.setColumnDataFromObject(2, table.getSchema());
1263:                    rd.setColumnDataFromObject(3, table.getName());
1264:                    rd.setColumnDataFromObject(4, ref_table.getSchema());
1265:                    rd.setColumnDataFromObject(5, ref_table.getName());
1266:                    rd.setColumnDataFromObject(6, update_rule);
1267:                    rd.setColumnDataFromObject(7, delete_rule);
1268:                    rd.setColumnDataFromObject(8, BigNumber.fromInt(deferred));
1269:                    t.addRow(rd);
1270:
1271:                    // Insert the columns
1272:                    for (int i = 0; i < cols.length; ++i) {
1273:                        rd = new RowData(tcols);
1274:                        rd.setColumnDataFromObject(0, unique_id); // unique id
1275:                        rd.setColumnDataFromObject(1, cols[i]); // column name
1276:                        rd.setColumnDataFromObject(2, ref_cols[i]); // ref column name
1277:                        rd.setColumnDataFromObject(3, BigNumber.fromInt(i)); // sequence number
1278:                        tcols.addRow(rd);
1279:                    }
1280:
1281:                } catch (DatabaseConstraintViolationException e) {
1282:                    // Constraint violation when inserting the data.  Check the type and
1283:                    // wrap around an appropriate error message.
1284:                    if (e.getErrorCode() == DatabaseConstraintViolationException.UNIQUE_VIOLATION) {
1285:                        // This means we gave a constraint name that's already being used
1286:                        // for a primary key.
1287:                        throw new StatementException(
1288:                                "Foreign key constraint name '"
1289:                                        + constraint_name
1290:                                        + "' is already being used.");
1291:                    }
1292:                    throw e;
1293:                }
1294:
1295:            }
1296:
1297:            /**
1298:             * Adds a primary key constraint that becomes perminent when the transaction
1299:             * is committed.  A primary key represents a set of columns in a table
1300:             * that are constrained to be unique and can not be null.  If the
1301:             * constraint name parameter is 'null' a primary key constraint is created
1302:             * with a unique constraint name.
1303:             * <p>
1304:             * NOTE: Security checks for adding constraints must be checked for at a
1305:             *   higher layer.
1306:             * <p>
1307:             * NOTE: We must guarentee that the transaction be in exclusive mode before
1308:             *   this method is called.
1309:             */
1310:            public void addPrimaryKeyConstraint(TableName table_name,
1311:                    String[] cols, short deferred, String constraint_name) {
1312:
1313:                TableName tn1 = TableDataConglomerate.PRIMARY_INFO_TABLE;
1314:                TableName tn2 = TableDataConglomerate.PRIMARY_COLS_TABLE;
1315:                MutableTableDataSource t = getTable(tn1);
1316:                MutableTableDataSource tcols = getTable(tn2);
1317:
1318:                try {
1319:
1320:                    // Insert a value into PRIMARY_INFO_TABLE
1321:                    RowData rd = new RowData(t);
1322:                    BigNumber unique_id = BigNumber.fromLong(nextUniqueID(tn1));
1323:                    constraint_name = makeUniqueConstraintName(constraint_name,
1324:                            unique_id);
1325:                    rd.setColumnDataFromObject(0, unique_id);
1326:                    rd.setColumnDataFromObject(1, constraint_name);
1327:                    rd.setColumnDataFromObject(2, table_name.getSchema());
1328:                    rd.setColumnDataFromObject(3, table_name.getName());
1329:                    rd.setColumnDataFromObject(4, BigNumber.fromInt(deferred));
1330:                    t.addRow(rd);
1331:
1332:                    // Insert the columns
1333:                    for (int i = 0; i < cols.length; ++i) {
1334:                        rd = new RowData(tcols);
1335:                        rd.setColumnDataFromObject(0, unique_id); // unique id
1336:                        rd.setColumnDataFromObject(1, cols[i]); // column name
1337:                        rd.setColumnDataFromObject(2, BigNumber.fromInt(i)); // Sequence number
1338:                        tcols.addRow(rd);
1339:                    }
1340:
1341:                } catch (DatabaseConstraintViolationException e) {
1342:                    // Constraint violation when inserting the data.  Check the type and
1343:                    // wrap around an appropriate error message.
1344:                    if (e.getErrorCode() == DatabaseConstraintViolationException.UNIQUE_VIOLATION) {
1345:                        // This means we gave a constraint name that's already being used
1346:                        // for a primary key.
1347:                        throw new StatementException(
1348:                                "Primary key constraint name '"
1349:                                        + constraint_name
1350:                                        + "' is already being used.");
1351:                    }
1352:                    throw e;
1353:                }
1354:
1355:            }
1356:
1357:            /**
1358:             * Adds a check expression that becomes perminent when the transaction
1359:             * is committed.  A check expression is an expression that must evaluate
1360:             * to true for all records added/updated in the database.
1361:             * <p>
1362:             * NOTE: Security checks for adding constraints must be checked for at a
1363:             *   higher layer.
1364:             * <p>
1365:             * NOTE: We must guarentee that the transaction be in exclusive mode before
1366:             *   this method is called.
1367:             */
1368:            public void addCheckConstraint(TableName table_name,
1369:                    Expression expression, short deferred,
1370:                    String constraint_name) {
1371:
1372:                TableName tn = TableDataConglomerate.CHECK_INFO_TABLE;
1373:                MutableTableDataSource t = getTable(tn);
1374:                int col_count = t.getDataTableDef().columnCount();
1375:
1376:                try {
1377:
1378:                    // Insert check constraint data.
1379:                    BigNumber unique_id = BigNumber.fromLong(nextUniqueID(tn));
1380:                    constraint_name = makeUniqueConstraintName(constraint_name,
1381:                            unique_id);
1382:                    RowData rd = new RowData(t);
1383:                    rd.setColumnDataFromObject(0, unique_id);
1384:                    rd.setColumnDataFromObject(1, constraint_name);
1385:                    rd.setColumnDataFromObject(2, table_name.getSchema());
1386:                    rd.setColumnDataFromObject(3, table_name.getName());
1387:                    rd
1388:                            .setColumnDataFromObject(4, new String(expression
1389:                                    .text()));
1390:                    rd.setColumnDataFromObject(5, BigNumber.fromInt(deferred));
1391:                    if (col_count > 6) {
1392:                        // Serialize the check expression
1393:                        ByteLongObject serialized_expression = ObjectTranslator
1394:                                .serialize(expression);
1395:                        rd.setColumnDataFromObject(6, serialized_expression);
1396:                    }
1397:                    t.addRow(rd);
1398:
1399:                } catch (DatabaseConstraintViolationException e) {
1400:                    // Constraint violation when inserting the data.  Check the type and
1401:                    // wrap around an appropriate error message.
1402:                    if (e.getErrorCode() == DatabaseConstraintViolationException.UNIQUE_VIOLATION) {
1403:                        // This means we gave a constraint name that's already being used.
1404:                        throw new StatementException("Check constraint name '"
1405:                                + constraint_name + "' is already being used.");
1406:                    }
1407:                    throw e;
1408:                }
1409:
1410:            }
1411:
1412:            /**
1413:             * Drops all the constraints defined for the given table.  This is a useful
1414:             * function when dropping a table from the database.
1415:             * <p>
1416:             * NOTE: Security checks that the user can drop constraints must be checke at
1417:             *   a higher layer.
1418:             * <p>
1419:             * NOTE: We must guarentee that the transaction be in exclusive mode before
1420:             *   this method is called.
1421:             */
1422:            public void dropAllConstraintsForTable(TableName table_name) {
1423:                ColumnGroup primary = queryTablePrimaryKeyGroup(this ,
1424:                        table_name);
1425:                ColumnGroup[] uniques = queryTableUniqueGroups(this , table_name);
1426:                CheckExpression[] expressions = queryTableCheckExpressions(
1427:                        this , table_name);
1428:                ColumnGroupReference[] refs = queryTableForeignKeyReferences(
1429:                        this , table_name);
1430:
1431:                if (primary != null) {
1432:                    dropPrimaryKeyConstraintForTable(table_name, primary.name);
1433:                }
1434:                for (int i = 0; i < uniques.length; ++i) {
1435:                    dropUniqueConstraintForTable(table_name, uniques[i].name);
1436:                }
1437:                for (int i = 0; i < expressions.length; ++i) {
1438:                    dropCheckConstraintForTable(table_name, expressions[i].name);
1439:                }
1440:                for (int i = 0; i < refs.length; ++i) {
1441:                    dropForeignKeyReferenceConstraintForTable(table_name,
1442:                            refs[i].name);
1443:                }
1444:
1445:            }
1446:
1447:            /**
1448:             * Drops the named constraint from the transaction.  Used when altering
1449:             * table schema.  Returns the number of constraints that were removed from
1450:             * the system.  If this method returns 0 then it indicates there is no
1451:             * constraint with the given name in the table.
1452:             * <p>
1453:             * NOTE: Security checks that the user can drop constraints must be checke at
1454:             *   a higher layer.
1455:             * <p>
1456:             * NOTE: We must guarentee that the transaction be in exclusive mode before
1457:             *   this method is called.
1458:             */
1459:            public int dropNamedConstraint(TableName table_name,
1460:                    String constraint_name) {
1461:
1462:                int drop_count = 0;
1463:                if (dropPrimaryKeyConstraintForTable(table_name,
1464:                        constraint_name)) {
1465:                    ++drop_count;
1466:                }
1467:                if (dropUniqueConstraintForTable(table_name, constraint_name)) {
1468:                    ++drop_count;
1469:                }
1470:                if (dropCheckConstraintForTable(table_name, constraint_name)) {
1471:                    ++drop_count;
1472:                }
1473:                if (dropForeignKeyReferenceConstraintForTable(table_name,
1474:                        constraint_name)) {
1475:                    ++drop_count;
1476:                }
1477:                return drop_count;
1478:            }
1479:
1480:            /**
1481:             * Drops the primary key constraint for the given table.  Used when altering
1482:             * table schema.  If 'constraint_name' is null this method will search for
1483:             * the primary key of the table name.  Returns true if the primary key
1484:             * constraint was dropped (the constraint existed).
1485:             * <p>
1486:             * NOTE: Security checks that the user can drop constraints must be checke at
1487:             *   a higher layer.
1488:             * <p>
1489:             * NOTE: We must guarentee that the transaction be in exclusive mode before
1490:             *   this method is called.
1491:             */
1492:            public boolean dropPrimaryKeyConstraintForTable(
1493:                    TableName table_name, String constraint_name) {
1494:
1495:                MutableTableDataSource t = getTable(TableDataConglomerate.PRIMARY_INFO_TABLE);
1496:                MutableTableDataSource t2 = getTable(TableDataConglomerate.PRIMARY_COLS_TABLE);
1497:                SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1498:                SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1499:
1500:                try {
1501:                    IntegerVector data;
1502:                    if (constraint_name != null) {
1503:                        // Returns the list of indexes where column 1 = constraint name
1504:                        //                               and column 2 = schema name
1505:                        data = dt.selectIndexesEqual(1, constraint_name, 2,
1506:                                table_name.getSchema());
1507:                    } else {
1508:                        // Returns the list of indexes where column 3 = table name
1509:                        //                               and column 2 = schema name
1510:                        data = dt.selectIndexesEqual(3, table_name.getName(),
1511:                                2, table_name.getSchema());
1512:                    }
1513:
1514:                    if (data.size() > 1) {
1515:                        throw new Error(
1516:                                "Assertion failed: multiple primary key for: "
1517:                                        + table_name);
1518:                    } else if (data.size() == 1) {
1519:                        int row_index = data.intAt(0);
1520:                        // The id
1521:                        TObject id = dt.get(0, row_index);
1522:                        // All columns with this id
1523:                        IntegerVector ivec = dtcols.selectIndexesEqual(0, id);
1524:                        // Delete from the table
1525:                        dtcols.deleteRows(ivec);
1526:                        dt.deleteRows(data);
1527:                        return true;
1528:                    }
1529:                    // data.size() must be 0 so no constraint was found to drop.
1530:                    return false;
1531:
1532:                } finally {
1533:                    dtcols.dispose();
1534:                    dt.dispose();
1535:                }
1536:
1537:            }
1538:
1539:            /**
1540:             * Drops a single named unique constraint from the given table.  Returns
1541:             * true if the unique constraint was dropped (the constraint existed).
1542:             * <p>
1543:             * NOTE: Security checks that the user can drop constraints must be checke at
1544:             *   a higher layer.
1545:             * <p>
1546:             * NOTE: We must guarentee that the transaction be in exclusive mode before
1547:             *   this method is called.
1548:             */
1549:            public boolean dropUniqueConstraintForTable(TableName table,
1550:                    String constraint_name) {
1551:
1552:                MutableTableDataSource t = getTable(TableDataConglomerate.UNIQUE_INFO_TABLE);
1553:                MutableTableDataSource t2 = getTable(TableDataConglomerate.UNIQUE_COLS_TABLE);
1554:                SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1555:                SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1556:
1557:                try {
1558:                    // Returns the list of indexes where column 1 = constraint name
1559:                    //                               and column 2 = schema name
1560:                    IntegerVector data = dt.selectIndexesEqual(1,
1561:                            constraint_name, 2, table.getSchema());
1562:
1563:                    if (data.size() > 1) {
1564:                        throw new Error(
1565:                                "Assertion failed: multiple unique constraint name: "
1566:                                        + constraint_name);
1567:                    } else if (data.size() == 1) {
1568:                        int row_index = data.intAt(0);
1569:                        // The id
1570:                        TObject id = dt.get(0, row_index);
1571:                        // All columns with this id
1572:                        IntegerVector ivec = dtcols.selectIndexesEqual(0, id);
1573:                        // Delete from the table
1574:                        dtcols.deleteRows(ivec);
1575:                        dt.deleteRows(data);
1576:                        return true;
1577:                    }
1578:                    // data.size() == 0 so the constraint wasn't found
1579:                    return false;
1580:                } finally {
1581:                    dtcols.dispose();
1582:                    dt.dispose();
1583:                }
1584:
1585:            }
1586:
1587:            /**
1588:             * Drops a single named check constraint from the given table.  Returns true
1589:             * if the check constraint was dropped (the constraint existed).
1590:             * <p>
1591:             * NOTE: Security checks that the user can drop constraints must be checke at
1592:             *   a higher layer.
1593:             * <p>
1594:             * NOTE: We must guarentee that the transaction be in exclusive mode before
1595:             *   this method is called.
1596:             */
1597:            public boolean dropCheckConstraintForTable(TableName table,
1598:                    String constraint_name) {
1599:
1600:                MutableTableDataSource t = getTable(TableDataConglomerate.CHECK_INFO_TABLE);
1601:                SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1602:
1603:                try {
1604:                    // Returns the list of indexes where column 1 = constraint name
1605:                    //                               and column 2 = schema name
1606:                    IntegerVector data = dt.selectIndexesEqual(1,
1607:                            constraint_name, 2, table.getSchema());
1608:
1609:                    if (data.size() > 1) {
1610:                        throw new Error(
1611:                                "Assertion failed: multiple check constraint name: "
1612:                                        + constraint_name);
1613:                    } else if (data.size() == 1) {
1614:                        // Delete the check constraint
1615:                        dt.deleteRows(data);
1616:                        return true;
1617:                    }
1618:                    // data.size() == 0 so the constraint wasn't found
1619:                    return false;
1620:                } finally {
1621:                    dt.dispose();
1622:                }
1623:
1624:            }
1625:
1626:            /**
1627:             * Drops a single named foreign key reference from the given table.  Returns
1628:             * true if the foreign key reference constraint was dropped (the constraint
1629:             * existed).
1630:             * <p>
1631:             * NOTE: Security checks that the user can drop constraints must be checke at
1632:             *   a higher layer.
1633:             * <p>
1634:             * NOTE: We must guarentee that the transaction be in exclusive mode before
1635:             *   this method is called.
1636:             */
1637:            public boolean dropForeignKeyReferenceConstraintForTable(
1638:                    TableName table, String constraint_name) {
1639:
1640:                MutableTableDataSource t = getTable(TableDataConglomerate.FOREIGN_INFO_TABLE);
1641:                MutableTableDataSource t2 = getTable(TableDataConglomerate.FOREIGN_COLS_TABLE);
1642:                SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1643:                SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1644:
1645:                try {
1646:                    // Returns the list of indexes where column 1 = constraint name
1647:                    //                               and column 2 = schema name
1648:                    IntegerVector data = dt.selectIndexesEqual(1,
1649:                            constraint_name, 2, table.getSchema());
1650:
1651:                    if (data.size() > 1) {
1652:                        throw new Error(
1653:                                "Assertion failed: multiple foreign key constraint "
1654:                                        + "name: " + constraint_name);
1655:                    } else if (data.size() == 1) {
1656:                        int row_index = data.intAt(0);
1657:                        // The id
1658:                        TObject id = dt.get(0, row_index);
1659:                        // All columns with this id
1660:                        IntegerVector ivec = dtcols.selectIndexesEqual(0, id);
1661:                        // Delete from the table
1662:                        dtcols.deleteRows(ivec);
1663:                        dt.deleteRows(data);
1664:                        return true;
1665:                    }
1666:                    // data.size() == 0 so the constraint wasn't found
1667:                    return false;
1668:                } finally {
1669:                    dtcols.dispose();
1670:                    dt.dispose();
1671:                }
1672:
1673:            }
1674:
1675:            /**
1676:             * Returns the list of tables (as a TableName array) that are dependant
1677:             * on the data in the given table to maintain referential consistancy.  The
1678:             * list includes the tables referenced as foreign keys, and the tables
1679:             * that reference the table as a foreign key.
1680:             * <p>
1681:             * This is a useful query for determining ahead of time the tables that
1682:             * require a read lock when inserting/updating a table.  A table will require
1683:             * a read lock if the operation needs to query it for potential referential
1684:             * integrity violations.
1685:             */
1686:            public static TableName[] queryTablesRelationallyLinkedTo(
1687:                    SimpleTransaction transaction, TableName table) {
1688:                ArrayList list = new ArrayList();
1689:                ColumnGroupReference[] refs = queryTableForeignKeyReferences(
1690:                        transaction, table);
1691:                for (int i = 0; i < refs.length; ++i) {
1692:                    TableName tname = refs[i].ref_table_name;
1693:                    if (!list.contains(tname)) {
1694:                        list.add(tname);
1695:                    }
1696:                }
1697:                refs = queryTableImportedForeignKeyReferences(transaction,
1698:                        table);
1699:                for (int i = 0; i < refs.length; ++i) {
1700:                    TableName tname = refs[i].key_table_name;
1701:                    if (!list.contains(tname)) {
1702:                        list.add(tname);
1703:                    }
1704:                }
1705:                return (TableName[]) list.toArray(new TableName[list.size()]);
1706:            }
1707:
1708:            /**
1709:             * Returns a set of unique groups that are constrained to be unique for
1710:             * the given table in this transaction.  For example, if columns ('name')
1711:             * and ('number', 'document_rev') are defined as unique, this will return
1712:             * an array of two groups that represent unique columns in the given
1713:             * table.
1714:             */
1715:            public static ColumnGroup[] queryTableUniqueGroups(
1716:                    SimpleTransaction transaction, TableName table_name) {
1717:                TableDataSource t = transaction
1718:                        .getTableDataSource(TableDataConglomerate.UNIQUE_INFO_TABLE);
1719:                TableDataSource t2 = transaction
1720:                        .getTableDataSource(TableDataConglomerate.UNIQUE_COLS_TABLE);
1721:                SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1722:                SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1723:
1724:                ColumnGroup[] groups;
1725:                try {
1726:                    // Returns the list indexes where column 3 = table name
1727:                    //                            and column 2 = schema name
1728:                    IntegerVector data = dt.selectIndexesEqual(3, table_name
1729:                            .getName(), 2, table_name.getSchema());
1730:                    groups = new ColumnGroup[data.size()];
1731:
1732:                    for (int i = 0; i < data.size(); ++i) {
1733:                        TObject id = dt.get(0, data.intAt(i));
1734:
1735:                        // Select all records with equal id
1736:                        IntegerVector cols = dtcols.selectIndexesEqual(0, id);
1737:
1738:                        // Put into a group.
1739:                        ColumnGroup group = new ColumnGroup();
1740:                        // constraint name
1741:                        group.name = dt.get(1, data.intAt(i)).getObject()
1742:                                .toString();
1743:                        group.columns = toColumns(dtcols, cols); // the list of columns
1744:                        group.deferred = ((BigNumber) dt.get(4, data.intAt(i))
1745:                                .getObject()).shortValue();
1746:                        groups[i] = group;
1747:                    }
1748:                } finally {
1749:                    dt.dispose();
1750:                    dtcols.dispose();
1751:                }
1752:
1753:                return groups;
1754:            }
1755:
1756:            /**
1757:             * Returns a set of primary key groups that are constrained to be unique
1758:             * for the given table in this transaction (there can be only 1 primary
1759:             * key defined for a table).  Returns null if there is no primary key
1760:             * defined for the table.
1761:             */
1762:            public static ColumnGroup queryTablePrimaryKeyGroup(
1763:                    SimpleTransaction transaction, TableName table_name) {
1764:                TableDataSource t = transaction
1765:                        .getTableDataSource(TableDataConglomerate.PRIMARY_INFO_TABLE);
1766:                TableDataSource t2 = transaction
1767:                        .getTableDataSource(TableDataConglomerate.PRIMARY_COLS_TABLE);
1768:                SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1769:                SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1770:
1771:                try {
1772:                    // Returns the list indexes where column 3 = table name
1773:                    //                            and column 2 = schema name
1774:                    IntegerVector data = dt.selectIndexesEqual(3, table_name
1775:                            .getName(), 2, table_name.getSchema());
1776:
1777:                    if (data.size() > 1) {
1778:                        throw new Error(
1779:                                "Assertion failed: multiple primary key for: "
1780:                                        + table_name);
1781:                    } else if (data.size() == 1) {
1782:                        int row_index = data.intAt(0);
1783:                        // The id
1784:                        TObject id = dt.get(0, row_index);
1785:                        // All columns with this id
1786:                        IntegerVector ivec = dtcols.selectIndexesEqual(0, id);
1787:                        // Make it in to a columns object
1788:                        ColumnGroup group = new ColumnGroup();
1789:                        group.name = dt.get(1, row_index).getObject()
1790:                                .toString();
1791:                        group.columns = toColumns(dtcols, ivec);
1792:                        group.deferred = ((BigNumber) dt.get(4, row_index)
1793:                                .getObject()).shortValue();
1794:                        return group;
1795:                    } else {
1796:                        return null;
1797:                    }
1798:                } finally {
1799:                    dt.dispose();
1800:                    dtcols.dispose();
1801:                }
1802:
1803:            }
1804:
1805:            /**
1806:             * Returns a set of check expressions that are constrained over all new
1807:             * columns added to the given table in this transaction.  For example,
1808:             * we may want a column called 'serial_number' to be constrained as
1809:             * CHECK serial_number LIKE '___-________-___'.
1810:             */
1811:            public static CheckExpression[] queryTableCheckExpressions(
1812:                    SimpleTransaction transaction, TableName table_name) {
1813:                TableDataSource t = transaction
1814:                        .getTableDataSource(TableDataConglomerate.CHECK_INFO_TABLE);
1815:                SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1816:
1817:                CheckExpression[] checks;
1818:                try {
1819:                    // Returns the list indexes where column 3 = table name
1820:                    //                            and column 2 = schema name
1821:                    IntegerVector data = dt.selectIndexesEqual(3, table_name
1822:                            .getName(), 2, table_name.getSchema());
1823:                    checks = new CheckExpression[data.size()];
1824:
1825:                    for (int i = 0; i < checks.length; ++i) {
1826:                        int row_index = data.intAt(i);
1827:
1828:                        CheckExpression check = new CheckExpression();
1829:                        check.name = dt.get(1, row_index).getObject()
1830:                                .toString();
1831:                        check.deferred = ((BigNumber) dt.get(5, row_index)
1832:                                .getObject()).shortValue();
1833:                        // Is the deserialized version available?
1834:                        if (t.getDataTableDef().columnCount() > 6) {
1835:                            ByteLongObject sexp = (ByteLongObject) dt.get(6,
1836:                                    row_index).getObject();
1837:                            if (sexp != null) {
1838:                                try {
1839:                                    // Deserialize the expression
1840:                                    check.expression = (Expression) ObjectTranslator
1841:                                            .deserialize(sexp);
1842:                                } catch (Throwable e) {
1843:                                    // We weren't able to deserialize the expression so report the
1844:                                    // error to the log
1845:                                    transaction.Debug().write(
1846:                                            Lvl.WARNING,
1847:                                            Transaction.class,
1848:                                            "Unable to deserialize the check expression.  "
1849:                                                    + "The error is: "
1850:                                                    + e.getMessage());
1851:                                    transaction
1852:                                            .Debug()
1853:                                            .write(Lvl.WARNING,
1854:                                                    Transaction.class,
1855:                                                    "Parsing the check expression instead.");
1856:                                    check.expression = null;
1857:                                }
1858:                            }
1859:                        }
1860:                        // Otherwise we need to parse it from the string
1861:                        if (check.expression == null) {
1862:                            Expression exp = Expression.parse(dt.get(4,
1863:                                    row_index).getObject().toString());
1864:                            check.expression = exp;
1865:                        }
1866:                        checks[i] = check;
1867:                    }
1868:
1869:                } finally {
1870:                    dt.dispose();
1871:                }
1872:
1873:                return checks;
1874:            }
1875:
1876:            /**
1877:             * Returns an array of column references in the given table that represent
1878:             * foreign key references.  For example, say a foreign reference has been
1879:             * set up in the given table as follows;<p><pre>
1880:             *   FOREIGN KEY (customer_id) REFERENCES Customer (id)
1881:             * </pre><p>
1882:             * This method will return the column group reference
1883:             * Order(customer_id) -> Customer(id).
1884:             * <p>
1885:             * This method is used to check that a foreign key reference actually points
1886:             * to a valid record in the referenced table as expected.
1887:             */
1888:            public static ColumnGroupReference[] queryTableForeignKeyReferences(
1889:                    SimpleTransaction transaction, TableName table_name) {
1890:
1891:                TableDataSource t = transaction
1892:                        .getTableDataSource(TableDataConglomerate.FOREIGN_INFO_TABLE);
1893:                TableDataSource t2 = transaction
1894:                        .getTableDataSource(TableDataConglomerate.FOREIGN_COLS_TABLE);
1895:                SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1896:                SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1897:
1898:                ColumnGroupReference[] groups;
1899:                try {
1900:                    // Returns the list indexes where column 3 = table name
1901:                    //                            and column 2 = schema name
1902:                    IntegerVector data = dt.selectIndexesEqual(3, table_name
1903:                            .getName(), 2, table_name.getSchema());
1904:                    groups = new ColumnGroupReference[data.size()];
1905:
1906:                    for (int i = 0; i < data.size(); ++i) {
1907:                        int row_index = data.intAt(i);
1908:
1909:                        // The foreign key id
1910:                        TObject id = dt.get(0, row_index);
1911:
1912:                        // The referenced table
1913:                        TableName ref_table_name = new TableName(dt.get(4,
1914:                                row_index).getObject().toString(), dt.get(5,
1915:                                row_index).getObject().toString());
1916:
1917:                        // Select all records with equal id
1918:                        IntegerVector cols = dtcols.selectIndexesEqual(0, id);
1919:
1920:                        // Put into a group.
1921:                        ColumnGroupReference group = new ColumnGroupReference();
1922:                        // constraint name
1923:                        group.name = dt.get(1, row_index).getObject()
1924:                                .toString();
1925:                        group.key_table_name = table_name;
1926:                        group.ref_table_name = ref_table_name;
1927:                        group.update_rule = dt.get(6, row_index).getObject()
1928:                                .toString();
1929:                        group.delete_rule = dt.get(7, row_index).getObject()
1930:                                .toString();
1931:                        group.deferred = ((BigNumber) dt.get(8, row_index)
1932:                                .getObject()).shortValue();
1933:
1934:                        int cols_size = cols.size();
1935:                        String[] key_cols = new String[cols_size];
1936:                        String[] ref_cols = new String[cols_size];
1937:                        for (int n = 0; n < cols_size; ++n) {
1938:                            for (int p = 0; p < cols_size; ++p) {
1939:                                int cols_index = cols.intAt(p);
1940:                                if (((BigNumber) dtcols.get(3, cols_index)
1941:                                        .getObject()).intValue() == n) {
1942:                                    key_cols[n] = dtcols.get(1, cols_index)
1943:                                            .getObject().toString();
1944:                                    ref_cols[n] = dtcols.get(2, cols_index)
1945:                                            .getObject().toString();
1946:                                    break;
1947:                                }
1948:                            }
1949:                        }
1950:                        group.key_columns = key_cols;
1951:                        group.ref_columns = ref_cols;
1952:
1953:                        groups[i] = group;
1954:                    }
1955:                } finally {
1956:                    dt.dispose();
1957:                    dtcols.dispose();
1958:                }
1959:
1960:                return groups;
1961:            }
1962:
1963:            /**
1964:             * Returns an array of column references in the given table that represent
1965:             * foreign key references that reference columns in the given table.  This
1966:             * is a reverse mapping of the 'queryTableForeignKeyReferences' method.  For
1967:             * example, say a foreign reference has been set up in any table as follows;
1968:             * <p><pre>
1969:             *   [ In table Order ]
1970:             *   FOREIGN KEY (customer_id) REFERENCE Customer (id)
1971:             * </pre><p>
1972:             * And the table name we are querying is 'Customer' then this method will
1973:             * return the column group reference
1974:             * Order(customer_id) -> Customer(id).
1975:             * <p>
1976:             * This method is used to check that a reference isn't broken when we remove
1977:             * a record (for example, removing a Customer that has references to it will
1978:             * break integrity).
1979:             */
1980:            public static ColumnGroupReference[] queryTableImportedForeignKeyReferences(
1981:                    SimpleTransaction transaction, TableName ref_table_name) {
1982:
1983:                TableDataSource t = transaction
1984:                        .getTableDataSource(TableDataConglomerate.FOREIGN_INFO_TABLE);
1985:                TableDataSource t2 = transaction
1986:                        .getTableDataSource(TableDataConglomerate.FOREIGN_COLS_TABLE);
1987:                SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1988:                SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1989:
1990:                ColumnGroupReference[] groups;
1991:                try {
1992:                    // Returns the list indexes where column 5 = ref table name
1993:                    //                            and column 4 = ref schema name
1994:                    IntegerVector data = dt.selectIndexesEqual(5,
1995:                            ref_table_name.getName(), 4, ref_table_name
1996:                                    .getSchema());
1997:                    groups = new ColumnGroupReference[data.size()];
1998:
1999:                    for (int i = 0; i < data.size(); ++i) {
2000:                        int row_index = data.intAt(i);
2001:
2002:                        // The foreign key id
2003:                        TObject id = dt.get(0, row_index);
2004:
2005:                        // The referencee table
2006:                        TableName table_name = new TableName(dt.get(2,
2007:                                row_index).getObject().toString(), dt.get(3,
2008:                                row_index).getObject().toString());
2009:
2010:                        // Select all records with equal id
2011:                        IntegerVector cols = dtcols.selectIndexesEqual(0, id);
2012:
2013:                        // Put into a group.
2014:                        ColumnGroupReference group = new ColumnGroupReference();
2015:                        // constraint name
2016:                        group.name = dt.get(1, row_index).getObject()
2017:                                .toString();
2018:                        group.key_table_name = table_name;
2019:                        group.ref_table_name = ref_table_name;
2020:                        group.update_rule = dt.get(6, row_index).getObject()
2021:                                .toString();
2022:                        group.delete_rule = dt.get(7, row_index).getObject()
2023:                                .toString();
2024:                        group.deferred = ((BigNumber) dt.get(8, row_index)
2025:                                .getObject()).shortValue();
2026:
2027:                        int cols_size = cols.size();
2028:                        String[] key_cols = new String[cols_size];
2029:                        String[] ref_cols = new String[cols_size];
2030:                        for (int n = 0; n < cols_size; ++n) {
2031:                            for (int p = 0; p < cols_size; ++p) {
2032:                                int cols_index = cols.intAt(p);
2033:                                if (((BigNumber) dtcols.get(3, cols_index)
2034:                                        .getObject()).intValue() == n) {
2035:                                    key_cols[n] = dtcols.get(1, cols_index)
2036:                                            .getObject().toString();
2037:                                    ref_cols[n] = dtcols.get(2, cols_index)
2038:                                            .getObject().toString();
2039:                                    break;
2040:                                }
2041:                            }
2042:                        }
2043:                        group.key_columns = key_cols;
2044:                        group.ref_columns = ref_cols;
2045:
2046:                        groups[i] = group;
2047:                    }
2048:                } finally {
2049:                    dt.dispose();
2050:                    dtcols.dispose();
2051:                }
2052:
2053:                return groups;
2054:            }
2055:
2056:            // ----- Transaction close operations -----
2057:
2058:            /**
2059:             * Closes and marks a transaction as committed.  Any changes made by this
2060:             * transaction are seen by all transactions created after this method
2061:             * returns.
2062:             * <p>
2063:             * This method will fail under the following circumstances:
2064:             * <ol>
2065:             * <li> There are any rows deleted in this transaction that were deleted
2066:             *  by another successfully committed transaction.
2067:             * <li> There were rows added in another committed transaction that would
2068:             *  change the result of the search clauses committed by this transaction.
2069:             * </ol>
2070:             * The first check is not too difficult to check for.  The second is very
2071:             * difficult however we need it to ensure TRANSACTION_SERIALIZABLE isolation
2072:             * is enforced.  We may have to simplify this by throwing a transaction
2073:             * exception if the table has had any changes made to it during this
2074:             * transaction.
2075:             * <p>
2076:             * This should only be called under an exclusive lock on the connection.
2077:             */
2078:            public void closeAndCommit() throws TransactionException {
2079:
2080:                if (!closed) {
2081:                    try {
2082:                        closed = true;
2083:                        // Get the conglomerate to do this commit.
2084:                        conglomerate.processCommit(this , getVisibleTables(),
2085:                                selected_from_tables, touched_tables, journal);
2086:                    } finally {
2087:                        cleanup();
2088:                    }
2089:                }
2090:
2091:            }
2092:
2093:            /**
2094:             * Closes and rolls back a transaction as if the commands the transaction ran
2095:             * never happened.  This will not throw a transaction exception.
2096:             * <p>
2097:             * This should only be called under an exclusive lock on the connection.
2098:             */
2099:            public void closeAndRollback() {
2100:
2101:                if (!closed) {
2102:                    try {
2103:                        closed = true;
2104:                        // Notify the conglomerate that this transaction has closed.
2105:                        conglomerate.processRollback(this , touched_tables,
2106:                                journal);
2107:                    } finally {
2108:                        cleanup();
2109:                    }
2110:                }
2111:
2112:            }
2113:
2114:            /**
2115:             * Cleans up this transaction.
2116:             */
2117:            private void cleanup() {
2118:                getSystem().stats().decrement("Transaction.count");
2119:                // Dispose of all the IndexSet objects created by this transaction.
2120:                disposeAllIndices();
2121:
2122:                // Dispose all the table we touched
2123:                try {
2124:                    for (int i = 0; i < touched_tables.size(); ++i) {
2125:                        MutableTableDataSource source = (MutableTableDataSource) touched_tables
2126:                                .get(i);
2127:                        source.dispose();
2128:                    }
2129:                } catch (Throwable e) {
2130:                    Debug().writeException(e);
2131:                }
2132:
2133:                getSystem().stats().increment("Transaction.cleanup");
2134:                conglomerate = null;
2135:                touched_tables = null;
2136:                journal = null;
2137:            }
2138:
2139:            /**
2140:             * Disposes this transaction without rolling back or committing the changes.
2141:             * Care should be taken when using this - it must only be used for simple
2142:             * transactions that are short lived and have not modified the database.
2143:             */
2144:            void dispose() {
2145:                if (!isReadOnly()) {
2146:                    throw new RuntimeException(
2147:                            "Assertion failed - tried to dispose a non read-only transaction.");
2148:                }
2149:                if (!closed) {
2150:                    closed = true;
2151:                    cleanup();
2152:                }
2153:            }
2154:
2155:            /**
2156:             * Finalize, we should close the transaction.
2157:             */
2158:            public void finalize() throws Throwable {
2159:                super .finalize();
2160:                if (!closed) {
2161:                    Debug().write(Lvl.ERROR, this , "Transaction not closed!");
2162:                    closeAndRollback();
2163:                }
2164:            }
2165:
2166:            // ---------- Transaction inner classes ----------
2167:
2168:            /**
2169:             * A list of DataTableDef system table definitions for tables internal to
2170:             * the transaction.
2171:             */
2172:            private final static DataTableDef[] INTERNAL_DEF_LIST;
2173:
2174:            static {
2175:                INTERNAL_DEF_LIST = new DataTableDef[3];
2176:                INTERNAL_DEF_LIST[0] = GTTableColumnsDataSource.DEF_DATA_TABLE_DEF;
2177:                INTERNAL_DEF_LIST[1] = GTTableInfoDataSource.DEF_DATA_TABLE_DEF;
2178:                INTERNAL_DEF_LIST[2] = GTProductDataSource.DEF_DATA_TABLE_DEF;
2179:            }
2180:
2181:            /**
2182:             * A static internal table info for internal tables to the transaction.
2183:             * This implementation includes all the dynamically generated system tables
2184:             * that are tied to information in a transaction.
2185:             */
2186:            private class TransactionInternalTables extends
2187:                    AbstractInternalTableInfo {
2188:
2189:                /**
2190:                 * Constructor.
2191:                 */
2192:                public TransactionInternalTables() {
2193:                    super ("SYSTEM TABLE", INTERNAL_DEF_LIST);
2194:                }
2195:
2196:                // ---------- Implemented ----------
2197:
2198:                public MutableTableDataSource createInternalTable(int index) {
2199:                    if (index == 0) {
2200:                        return new GTTableColumnsDataSource(Transaction.this )
2201:                                .init();
2202:                    } else if (index == 1) {
2203:                        return new GTTableInfoDataSource(Transaction.this )
2204:                                .init();
2205:                    } else if (index == 2) {
2206:                        return new GTProductDataSource(Transaction.this ).init();
2207:                    } else {
2208:                        throw new RuntimeException();
2209:                    }
2210:                }
2211:
2212:            }
2213:
2214:            /**
2215:             * A group of columns as used by the constraint system.  A ColumnGroup is
2216:             * a simple list of columns in a table.
2217:             */
2218:            public static class ColumnGroup {
2219:
2220:                /**
2221:                 * The name of the group (the constraint name).
2222:                 */
2223:                public String name;
2224:
2225:                /**
2226:                 * The list of columns that make up the group.
2227:                 */
2228:                public String[] columns;
2229:
2230:                /**
2231:                 * Whether this is deferred or initially immediate.
2232:                 */
2233:                public short deferred;
2234:
2235:            }
2236:
2237:            /**
2238:             * Represents a constraint expression to check.
2239:             */
2240:            public static class CheckExpression {
2241:
2242:                /**
2243:                 * The name of the check expression (the constraint name).
2244:                 */
2245:                public String name;
2246:
2247:                /**
2248:                 * The expression to check.
2249:                 */
2250:                public Expression expression;
2251:
2252:                /**
2253:                 * Whether this is deferred or initially immediate.
2254:                 */
2255:                public short deferred;
2256:
2257:            }
2258:
2259:            /**
2260:             * Represents a reference from a group of columns in one table to a group of
2261:             * columns in another table.  The is used to represent a foreign key
2262:             * reference.
2263:             */
2264:            public static class ColumnGroupReference {
2265:
2266:                /**
2267:                 * The name of the group (the constraint name).
2268:                 */
2269:                public String name;
2270:
2271:                /**
2272:                 * The key table name.
2273:                 */
2274:                public TableName key_table_name;
2275:
2276:                /**
2277:                 * The list of columns that make up the key.
2278:                 */
2279:                public String[] key_columns;
2280:
2281:                /**
2282:                 * The referenced table name.
2283:                 */
2284:                public TableName ref_table_name;
2285:
2286:                /**
2287:                 * The list of columns that make up the referenced group.
2288:                 */
2289:                public String[] ref_columns;
2290:
2291:                /**
2292:                 * The update rule.
2293:                 */
2294:                public String update_rule;
2295:
2296:                /**
2297:                 * The delete rule.
2298:                 */
2299:                public String delete_rule;
2300:
2301:                /**
2302:                 * Whether this is deferred or initially immediate.
2303:                 */
2304:                public short deferred;
2305:
2306:            }
2307:
2308:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.