KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mckoi > database > Transaction


1 /**
2  * com.mckoi.database.Transaction 18 Nov 2000
3  *
4  * Mckoi SQL Database ( http://www.mckoi.com/database )
5  * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * Version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License Version 2 for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * Version 2 along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Change Log:
21  *
22  *
23  */

24
25 package com.mckoi.database;
26
27 import com.mckoi.debug.*;
28 import com.mckoi.util.IntegerVector;
29 import com.mckoi.util.BigNumber;
30 import com.mckoi.database.global.ByteLongObject;
31 import com.mckoi.database.global.ObjectTranslator;
32 import java.io.IOException JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.HashMap JavaDoc;
35
36 /**
37  * An open transaction that manages all data access to the
38  * TableDataConglomerate. A transaction sees a view of the data as it was when
39  * the transaction was created. It also sees any modifications that were made
40  * within the context of this transaction. It does not see modifications made
41  * by other open transactions.
42  * <p>
43  * A transaction ends when it is committed or rollbacked. All operations
44  * on this transaction object only occur within the context of this transaction
45  * and are not permanent changes to the database structure. Only when the
46  * transaction is committed are changes reflected in the master data.
47  *
48  * @author Tobias Downer
49  */

50
51 public class Transaction extends SimpleTransaction {
52
53   // ---------- Constraint statics ----------
54
// These statics are for managing constraints.
55

56   /**
57    * The type of deferrance.
58    */

59   public static final short INITIALLY_DEFERRED =
60                   java.sql.DatabaseMetaData.importedKeyInitiallyDeferred;
61   public static final short INITIALLY_IMMEDIATE =
62                  java.sql.DatabaseMetaData.importedKeyInitiallyImmediate;
63   public static final short NOT_DEFERRABLE =
64                       java.sql.DatabaseMetaData.importedKeyNotDeferrable;
65
66   /**
67    * Foreign key referential trigger actions.
68    */

69   public static final String JavaDoc NO_ACTION = "NO ACTION";
70   public static final String JavaDoc CASCADE = "CASCADE";
71   public static final String JavaDoc SET_NULL = "SET NULL";
72   public static final String JavaDoc SET_DEFAULT = "SET DEFAULT";
73
74   // ---------- Member variables ----------
75

76   /**
77    * The TableDataConglomerate that this transaction is within the context of.
78    */

79   private TableDataConglomerate conglomerate;
80
81   /**
82    * The commit_id that represents the id of the last commit that occurred
83    * when this transaction was created.
84    */

85   private long commit_id;
86
87   /**
88    * All tables touched by this transaction. (MutableTableDataSource)
89    */

90   private ArrayList JavaDoc touched_tables;
91
92   /**
93    * All tables selected from in this transaction. (MasterTableDataSource)
94    */

95   private ArrayList JavaDoc selected_from_tables;
96
97   /**
98    * The name of all database objects that were created in this transaction.
99    * This is used for a namespace collision test during commit.
100    */

101   private ArrayList JavaDoc created_database_objects;
102
103   /**
104    * The name of all database objects that were dropped in this transaction.
105    * This is used for a namespace collision test during commit.
106    */

107   private ArrayList JavaDoc dropped_database_objects;
108
109   /**
110    * The journal for this transaction. This journal describes all changes
111    * made to the database by this transaction.
112    */

113   private TransactionJournal journal;
114
115   /**
116    * The list of InternalTableInfo objects that are containers for generating
117    * internal tables (GTDataSource).
118    */

119   private InternalTableInfo[] internal_tables;
120
121   /**
122    * A pointer in the internal_tables list.
123    */

124   private int internal_tables_i;
125
126   /**
127    * True if an error should be generated on a dirty select.
128    */

129   private boolean transaction_error_on_dirty_select;
130
131   /**
132    * True if this transaction is closed.
133    */

134   private boolean closed;
135
136
137   /**
138    * Constructs the transaction.
139    */

140   Transaction(TableDataConglomerate conglomerate,
141               long commit_id, ArrayList JavaDoc visible_tables,
142               ArrayList JavaDoc table_indices) {
143
144     super(conglomerate.getSystem(), conglomerate.getSequenceManager());
145
146     this.conglomerate = conglomerate;
147     this.commit_id = commit_id;
148     this.closed = false;
149
150     this.created_database_objects = new ArrayList JavaDoc();
151     this.dropped_database_objects = new ArrayList JavaDoc();
152
153     this.touched_tables = new ArrayList JavaDoc();
154     this.selected_from_tables = new ArrayList JavaDoc();
155     journal = new TransactionJournal();
156
157     // Set up all the visible tables
158
int sz = visible_tables.size();
159     for (int i = 0; i < sz; ++i) {
160       addVisibleTable((MasterTableDataSource) visible_tables.get(i),
161                       (IndexSet) table_indices.get(i));
162     }
163
164     // NOTE: We currently only support 8 - internal tables to the transaction
165
// layer, and internal tables to the database connection layer.
166
internal_tables = new InternalTableInfo[8];
167     internal_tables_i = 0;
168     addInternalTableInfo(new TransactionInternalTables());
169
170     getSystem().stats().increment("Transaction.count");
171
172     // Defaults to true (should be changed by called 'setErrorOnDirtySelect'
173
// method.
174
transaction_error_on_dirty_select = true;
175   }
176
177   /**
178    * Returns the TableDataConglomerate of this transaction.
179    */

180   final TableDataConglomerate getConglomerate() {
181     return conglomerate;
182   }
183   
184   /**
185    * Adds an internal table container (InternalTableInfo) used to
186    * resolve internal tables. This is intended as a way for the
187    * DatabaseConnection layer to plug in 'virtual' tables, such as those
188    * showing connection statistics, etc. It also allows modelling database
189    * objects as tables, such as sequences, triggers, procedures, etc.
190    */

191   void addInternalTableInfo(InternalTableInfo info) {
192     if (internal_tables_i >= internal_tables.length) {
193       throw new RuntimeException JavaDoc("Internal table list bounds reached.");
194     }
195     internal_tables[internal_tables_i] = info;
196     ++internal_tables_i;
197   }
198
199   /**
200    * Returns the 'commit_id' which is the last commit that occured before
201    * this transaction was created.
202    * <p>
203    * NOTE: Don't make this synchronized over anything. This is accessed
204    * by OpenTransactionList.
205    */

206   long getCommitID() {
207     // REINFORCED NOTE: This absolutely must never be synchronized because
208
// it is accessed by OpenTransactionList synchronized.
209
return commit_id;
210   }
211
212   // ----- Operations within the context of this transaction -----
213

214   /**
215    * Overwritten from SimpleTransaction.
216    * Returns a new MutableTableDataSource for the view of the
217    * MasterTableDataSource at the start of this transaction. Note that this is
218    * only ever called once per table accessed in this transaction.
219    */

220   public MutableTableDataSource
221            createMutableTableDataSourceAtCommit(MasterTableDataSource master) {
222     // Create the table for this transaction.
223
MutableTableDataSource table = master.createTableDataSourceAtCommit(this);
224     // Log in the journal that this table was touched by the transaction.
225
journal.entryAddTouchedTable(master.getTableID());
226     touched_tables.add(table);
227     return table;
228   }
229
230   /**
231    * Called by the query evaluation layer when information is selected
232    * from this table as part of this transaction. When there is a select
233    * query on a table, when the transaction is committed we should look for
234    * any concurrently committed changes to the table. If there are any, then
235    * any selects on the table should be considered incorrect and cause a
236    * commit failure.
237    */

238   public void addSelectedFromTable(TableName table_name) {
239     // Special handling of internal tables,
240
if (isDynamicTable(table_name)) {
241       return;
242     }
243
244     MasterTableDataSource master = findVisibleTable(table_name, false);
245     if (master == null) {
246       throw new StatementException(
247                             "Table with name not available: " + table_name);
248     }
249 // System.out.println("Selected from table: " + table_name);
250
synchronized (selected_from_tables) {
251       if (!selected_from_tables.contains(master)) {
252         selected_from_tables.add(master);
253       }
254     }
255
256   }
257
258
259
260   /**
261    * Copies all the tables within this transaction view to the destination
262    * conglomerate object. Some care should be taken with security when using
263    * this method. This is useful for generating a backup of the current
264    * view of the database that can work without interfering with the general
265    * operation of the database.
266    */

267   void liveCopyAllDataTo(TableDataConglomerate dest_conglomerate) {
268     // Create a new TableDataConglomerate using the same settings from this
269
// TransactionSystem but on the new StoreSystem.
270
int sz = getVisibleTableCount();
271
272     // The list to copy (in the order to copy in).
273
// We put the 'SEQUENCE_INFO' at the very end of the table list to copy.
274
ArrayList JavaDoc copy_list = new ArrayList JavaDoc(sz);
275
276     MasterTableDataSource last_entry = null;
277     for (int i = 0; i < sz; ++i) {
278       MasterTableDataSource master_table = getVisibleTable(i);
279       TableName table_name = master_table.getDataTableDef().getTableName();
280       if (table_name.equals(TableDataConglomerate.SYS_SEQUENCE_INFO)) {
281         last_entry = master_table;
282       }
283       else {
284         copy_list.add(master_table);
285       }
286     }
287     copy_list.add(0, last_entry);
288
289     try {
290       // For each master table,
291
for (int i = 0; i < sz; ++i) {
292
293         MasterTableDataSource master_table =
294                                (MasterTableDataSource) copy_list.get(i);
295         TableName table_name = master_table.getDataTableDef().getTableName();
296         
297         // Create a destination transaction
298
Transaction dest_transaction = dest_conglomerate.createTransaction();
299
300         // The view of this table within this transaction.
301
IndexSet index_set = getIndexSetForTable(master_table);
302
303         // If the table already exists then drop it
304
if (dest_transaction.tableExists(table_name)) {
305           dest_transaction.dropTable(table_name);
306         }
307         
308         // Copy it into the destination conglomerate.
309
dest_transaction.copyTable(master_table, index_set);
310
311         // Close and commit the transaction in the destination conglomeration.
312
dest_transaction.closeAndCommit();
313
314         // Dispose the IndexSet
315
index_set.dispose();
316
317       }
318       
319     }
320     catch (TransactionException e) {
321       Debug().writeException(e);
322       throw new RuntimeException JavaDoc("Transaction Error when copying table: " +
323                                  e.getMessage());
324     }
325   }
326
327   // ---------- Dynamically generated tables ----------
328

329   /**
330    * Returns true if the given table name represents a dynamically generated
331    * system table.
332    */

333   protected boolean isDynamicTable(TableName table_name) {
334     for (int i = 0; i < internal_tables.length; ++i) {
335       InternalTableInfo info = internal_tables[i];
336       if (info != null) {
337         if (info.containsTableName(table_name)) {
338           return true;
339         }
340       }
341     }
342     return false;
343   }
344
345   /**
346    * Returns a list of all dynamic table names. This method returns a
347    * reference to a static, make sure you don't change the contents of the
348    * array!
349    */

350   protected TableName[] getDynamicTableList() {
351     int sz = 0;
352     for (int i = 0; i < internal_tables.length; ++i) {
353       InternalTableInfo info = internal_tables[i];
354       if (info != null) {
355         sz += info.getTableCount();
356       }
357     }
358
359     TableName[] list = new TableName[sz];
360     int index = 0;
361
362     for (int i = 0; i < internal_tables.length; ++i) {
363       InternalTableInfo info = internal_tables[i];
364       if (info != null) {
365         sz = info.getTableCount();
366         for (int n = 0; n < sz; ++n) {
367           list[index] = info.getTableName(n);
368           ++index;
369         }
370       }
371     }
372
373     return list;
374   }
375
376   /**
377    * Returns the DataTableDef for the given internal table.
378    */

379   protected DataTableDef getDynamicDataTableDef(TableName table_name) {
380
381     for (int i = 0; i < internal_tables.length; ++i) {
382       InternalTableInfo info = internal_tables[i];
383       if (info != null) {
384         int index = info.findTableName(table_name);
385         if (index != -1) {
386           return info.getDataTableDef(index);
387         }
388       }
389     }
390
391     throw new RuntimeException JavaDoc("Not an internal table: " + table_name);
392   }
393
394   /**
395    * Returns an instance of MutableDataTableSource that represents the
396    * contents of the internal table with the given name.
397    */

398   protected MutableTableDataSource getDynamicTable(TableName table_name) {
399
400     for (int i = 0; i < internal_tables.length; ++i) {
401       InternalTableInfo info = internal_tables[i];
402       if (info != null) {
403         int index = info.findTableName(table_name);
404         if (index != -1) {
405           return info.createInternalTable(index);
406         }
407       }
408     }
409
410     throw new RuntimeException JavaDoc("Not an internal table: " + table_name);
411   }
412
413   /**
414    * Returns a string type describing the type of the dynamic table.
415    */

416   public String JavaDoc getDynamicTableType(TableName table_name) {
417     // Otherwise we need to look up the table in the internal table list,
418
for (int i = 0; i < internal_tables.length; ++i) {
419       InternalTableInfo info = internal_tables[i];
420       if (info != null) {
421         int index = info.findTableName(table_name);
422         if (index != -1) {
423           return info.getTableType(index);
424         }
425       }
426     }
427     // No internal table found, so report the error.
428
throw new RuntimeException JavaDoc("No table '" + table_name +
429                                "' to report type for.");
430   }
431
432   
433   // ---------- Transaction manipulation ----------
434

435   /**
436    * Creates a new table within this transaction with the given sector size.
437    * If the table already exists then an exception is thrown.
438    * <p>
439    * This should only be called under an exclusive lock on the connection.
440    */

441   public void createTable(DataTableDef table_def,
442                           int data_sector_size, int index_sector_size) {
443
444     TableName table_name = table_def.getTableName();
445     MasterTableDataSource master = findVisibleTable(table_name, false);
446     if (master != null) {
447       throw new StatementException(
448                                "Table '" + table_name + "' already exists.");
449     }
450
451     table_def.setImmutable();
452
453     if (data_sector_size < 27) {
454       data_sector_size = 27;
455     }
456     else if (data_sector_size > 4096) {
457       data_sector_size = 4096;
458     }
459
460     // Create the new master table and add to list of visible tables.
461
master = conglomerate.createMasterTable(table_def, data_sector_size,
462                                             index_sector_size);
463     // Add this table (and an index set) for this table.
464
addVisibleTable(master, master.createIndexSet());
465
466     // Log in the journal that this transaction touched the table_id.
467
int table_id = master.getTableID();
468     journal.entryAddTouchedTable(table_id);
469
470     // Log in the journal that we created this table.
471
journal.entryTableCreate(table_id);
472
473     // Add entry to the Sequences table for the native generator for this
474
// table.
475
SequenceManager.addNativeTableGenerator(this, table_name);
476
477     // Notify that this database object has been successfully created.
478
databaseObjectCreated(table_name);
479
480   }
481
482   /**
483    * Creates a new table within this transaction. If the table already
484    * exists then an exception is thrown.
485    * <p>
486    * This should only be called under an exclusive lock on the connection.
487    */

488   public void createTable(DataTableDef table_def) {
489     // data sector size defaults to 251
490
// index sector size defaults to 1024
491
createTable(table_def, 251, 1024);
492   }
493
494   /**
495    * Given a DataTableDef, if the table exists then it is updated otherwise
496    * if it doesn't exist then it is created.
497    * <p>
498    * This should only be used as very fine grain optimization for creating/
499    * altering tables. If in the future the underlying table model is changed
500    * so that the given 'sector_size' value is unapplicable, then the value
501    * will be ignored.
502    */

503   public void alterCreateTable(DataTableDef table_def,
504                                int data_sector_size, int index_sector_size) {
505     if (!tableExists(table_def.getTableName())) {
506       createTable(table_def, data_sector_size, index_sector_size);
507     }
508     else {
509       alterTable(table_def.getTableName(), table_def,
510                  data_sector_size, index_sector_size);
511     }
512   }
513
514   /**
515    * Drops a table within this transaction. If the table does not exist then
516    * an exception is thrown.
517    * <p>
518    * This should only be called under an exclusive lock on the connection.
519    */

520   public void dropTable(TableName table_name) {
521 // System.out.println(this + " DROP: " + table_name);
522
MasterTableDataSource master = findVisibleTable(table_name, false);
523
524     if (master == null) {
525       throw new StatementException(
526                                "Table '" + table_name + "' doesn't exist.");
527     }
528
529     // Removes this table from the visible table list of this transaction
530
removeVisibleTable(master);
531
532     // Log in the journal that this transaction touched the table_id.
533
int table_id = master.getTableID();
534     journal.entryAddTouchedTable(table_id);
535
536     // Log in the journal that we dropped this table.
537
journal.entryTableDrop(table_id);
538
539     // Remove the native sequence generator (in this transaction) for this
540
// table.
541
SequenceManager.removeNativeTableGenerator(this, table_name);
542
543     // Notify that this database object has been dropped
544
databaseObjectDropped(table_name);
545
546   }
547
548   /**
549    * Generates an exact copy of the table within this transaction. It is
550    * recommended that the table is dropped before the copy is made. The
551    * purpose of this method is to generate a temporary table that can be
552    * modified without fear of another transaction changing the contents in
553    * another transaction. This also provides a convenient way to compact
554    * a table because any spare space is removed when the table is copied. It
555    * also allows us to make a copy of MasterTableDataSource into a foreign
556    * conglomerate which allows us to implement a backup procedure.
557    * <p>
558    * This method does NOT assume the given MasterTableDataSource is contained,
559    * or has once been contained within this conglomerate.
560    */

561   public void copyTable(
562                  MasterTableDataSource src_master_table, IndexSet index_set) {
563
564     DataTableDef table_def = src_master_table.getDataTableDef();
565     TableName table_name = table_def.getTableName();
566     MasterTableDataSource master = findVisibleTable(table_name, false);
567     if (master != null) {
568       throw new StatementException(
569                 "Unable to copy. Table '" + table_name + "' already exists.");
570     }
571
572     // Copy the master table and add to the list of visible tables.
573
master = conglomerate.copyMasterTable(src_master_table, index_set);
574     // Add this visible table
575
addVisibleTable(master, master.createIndexSet());
576
577     // Log in the journal that this transaction touched the table_id.
578
int table_id = master.getTableID();
579     journal.entryAddTouchedTable(table_id);
580
581     // Log in the journal that we created this table.
582
journal.entryTableCreate(table_id);
583
584     // Add entry to the Sequences table for the native generator for this
585
// table.
586
SequenceManager.addNativeTableGenerator(this, table_name);
587     
588     // Notify that this database object has been successfully created.
589
databaseObjectCreated(table_name);
590     
591   }
592   
593   /**
594    * Alter the table with the given name to the new definition and give the
595    * copied table a new data sector size. If the table does not exist then
596    * an exception is thrown.
597    * <p>
598    * This copies all columns that were in the original table to the new
599    * altered table if the name is the same. Any names that don't exist are
600    * set to the default value.
601    * <p>
602    * This should only be called under an exclusive lock on the connection.
603    */

604   public void alterTable(TableName table_name, DataTableDef table_def,
605                          int data_sector_size, int index_sector_size) {
606
607     table_def.setImmutable();
608
609     // The current schema context is the schema of the table name
610
String JavaDoc current_schema = table_name.getSchema();
611     SystemQueryContext context = new SystemQueryContext(this, current_schema);
612
613     // Get the next unique id of the unaltered table.
614
long next_id = nextUniqueID(table_name);
615
616     // Drop the current table
617
MutableTableDataSource c_table = getTable(table_name);
618     dropTable(table_name);
619     // And create the table table
620
createTable(table_def);
621     MutableTableDataSource altered_table = getTable(table_name);
622
623     // Get the new MasterTableDataSource object
624
MasterTableDataSource new_master_table =
625                                         findVisibleTable(table_name, false);
626     // Set the sequence id of the table
627
new_master_table.setUniqueID(next_id);
628
629     // Work out which columns we have to copy to where
630
int[] col_map = new int[table_def.columnCount()];
631     DataTableDef orig_td = c_table.getDataTableDef();
632     for (int i = 0; i < col_map.length; ++i) {
633       String JavaDoc col_name = table_def.columnAt(i).getName();
634       col_map[i] = orig_td.findColumnName(col_name);
635     }
636
637     try {
638       // First move all the rows from the old table to the new table,
639
// This does NOT update the indexes.
640
try {
641         RowEnumeration e = c_table.rowEnumeration();
642         while (e.hasMoreRows()) {
643           int row_index = e.nextRowIndex();
644           RowData row_data = new RowData(altered_table);
645           for (int i = 0; i < col_map.length; ++i) {
646             int col = col_map[i];
647             if (col != -1) {
648               row_data.setColumnData(i,
649                                      c_table.getCellContents(col, row_index));
650             }
651           }
652           row_data.setDefaultForRest(context);
653           // Note we use a low level 'addRow' method on the master table
654
// here. This does not touch the table indexes. The indexes are
655
// built later.
656
int new_row_number = new_master_table.addRow(row_data);
657           // Set the record as committed added
658
new_master_table.writeRecordType(new_row_number, 0x010);
659         }
660       }
661       catch (DatabaseException e) {
662         Debug().writeException(e);
663         throw new RuntimeException JavaDoc(e.getMessage());
664       }
665
666       // PENDING: We need to copy any existing index definitions that might
667
// have been set on the table being altered.
668

669       // Rebuild the indexes in the new master table,
670
new_master_table.buildIndexes();
671
672       // Get the snapshot index set on the new table and set it here
673
setIndexSetForTable(new_master_table, new_master_table.createIndexSet());
674
675       // Flush this out of the table cache
676
flushTableCache(table_name);
677
678       // Ensure the native sequence generator exists...
679
SequenceManager.removeNativeTableGenerator(this, table_name);
680       SequenceManager.addNativeTableGenerator(this, table_name);
681
682       // Notify that this database object has been successfully dropped and
683
// created.
684
databaseObjectDropped(table_name);
685       databaseObjectCreated(table_name);
686       
687     }
688     catch (IOException JavaDoc e) {
689       Debug().writeException(e);
690       throw new RuntimeException JavaDoc(e.getMessage());
691     }
692
693   }
694
695   /**
696    * Alters the table with the given name within this transaction to the
697    * specified table definition. If the table does not exist then an exception
698    * is thrown.
699    * <p>
700    * This should only be called under an exclusive lock on the connection.
701    */

702   public void alterTable(TableName table_name, DataTableDef table_def) {
703
704     // Make sure we remember the current sector size of the altered table so
705
// we can create the new table with the original size.
706
try {
707
708       int current_data_sector_size;
709       MasterTableDataSource master = findVisibleTable(table_name, false);
710       if (master instanceof V1MasterTableDataSource) {
711         current_data_sector_size =
712                        ((V1MasterTableDataSource) master).rawDataSectorSize();
713       }
714       else {
715         current_data_sector_size = -1;
716       }
717       // HACK: We use index sector size of 2043 for all altered tables
718
alterTable(table_name, table_def, current_data_sector_size, 2043);
719
720     }
721     catch (IOException JavaDoc e) {
722       throw new RuntimeException JavaDoc("IO Error: " + e.getMessage());
723     }
724
725   }
726
727
728
729
730
731   /**
732    * Checks all the rows in the table for immediate constraint violations
733    * and when the transaction is next committed check for all deferred
734    * constraint violations. This method is used when the constraints on a
735    * table changes and we need to determine if any constraint violations
736    * occurred. To the constraint checking system, this is like adding all
737    * the rows to the given table.
738    */

739   public void checkAllConstraints(TableName table_name) {
740     // Get the table
741
TableDataSource table = getTable(table_name);
742     // Get all the rows in the table
743
int[] rows = new int[table.getRowCount()];
744     RowEnumeration row_enum = table.rowEnumeration();
745     int i = 0;
746     while (row_enum.hasMoreRows()) {
747       rows[i] = row_enum.nextRowIndex();
748       ++i;
749     }
750     // Check the constraints of all the rows in the table.
751
TableDataConglomerate.checkAddConstraintViolations(
752                      this, table, rows, INITIALLY_IMMEDIATE);
753
754     // Add that we altered this table in the journal
755
MasterTableDataSource master = findVisibleTable(table_name, false);
756     if (master == null) {
757       throw new StatementException(
758                                 "Table '" + table_name + "' doesn't exist.");
759     }
760
761     // Log in the journal that this transaction touched the table_id.
762
int table_id = master.getTableID();
763
764     journal.entryAddTouchedTable(table_id);
765     // Log in the journal that we dropped this table.
766
journal.entryTableConstraintAlter(table_id);
767
768   }
769
770   /**
771    * Compacts the table with the given name within this transaction. If the
772    * table doesn't exist then an exception is thrown.
773    */

774   public void compactTable(TableName table_name) {
775
776     // Find the master table.
777
MasterTableDataSource current_table = findVisibleTable(table_name, false);
778     if (current_table == null) {
779       throw new StatementException(
780                                  "Table '" + table_name + "' doesn't exist.");
781     }
782
783     // If the table is worth compacting, or the table is a
784
// V1MasterTableDataSource
785
if (current_table.isWorthCompacting()) {
786       // The view of this table within this transaction.
787
IndexSet index_set = getIndexSetForTable(current_table);
788       // Drop the current table
789
dropTable(table_name);
790       // And copy to the new table
791
copyTable(current_table, index_set);
792     }
793
794   }
795
796
797   
798   /**
799    * Returns true if the conglomerate commit procedure should check for
800    * dirty selects and produce a transaction error. A dirty select is when
801    * a query reads information from a table that is effected by another table
802    * during a transaction. This in itself will not cause data
803    * consistancy problems but for strict conformance to SERIALIZABLE
804    * isolation level this should return true.
805    * <p>
806    * NOTE; We MUST NOT make this method serialized because it is back called
807    * from within a commit lock in TableDataConglomerate.
808    */

809   boolean transactionErrorOnDirtySelect() {
810     return transaction_error_on_dirty_select;
811   }
812
813   /**
814    * Sets the transaction error on dirty select for this transaction.
815    */

816   void setErrorOnDirtySelect(boolean status) {
817     transaction_error_on_dirty_select = status;
818   }
819
820   // ----- Setting/Querying constraint information -----
821
// PENDING: Is it worth implementing a pluggable constraint architecture
822
// as described in the idea below. With the current implementation we
823
// have tied a DataTableConglomerate to a specific constraint
824
// architecture.
825
//
826
// IDEA: These methods delegate to the parent conglomerate which has a
827
// pluggable architecture for setting/querying constraints. Some uses of
828
// a conglomerate may not need integrity constraints or may implement the
829
// mechanism for storing/querying in a different way. This provides a
830
// useful abstraction of being enable to implement constraint behaviour
831
// by only providing a way to set/query the constraint information in
832
// different conglomerate uses.
833

834   /**
835    * Convenience, given a SimpleTableQuery object this will return a list of
836    * column names in sequence that represent the columns in a group constraint.
837    * <p>
838    * 'cols' is the unsorted list of indexes in the table that represent the
839    * group.
840    * <p>
841    * Assumes column 2 of dt is the sequence number and column 1 is the name
842    * of the column.
843    */

844   private static String JavaDoc[] toColumns(SimpleTableQuery dt, IntegerVector cols) {
845     int size = cols.size();
846     String JavaDoc[] list = new String JavaDoc[size];
847
848     // for each n of the output list
849
for (int n = 0; n < size; ++n) {
850       // for each i of the input list
851
for (int i = 0; i < size; ++i) {
852         int row_index = cols.intAt(i);
853         int seq_no = ((BigNumber) dt.get(2, row_index).getObject()).intValue();
854         if (seq_no == n) {
855           list[n] = dt.get(1, row_index).getObject().toString();
856           break;
857         }
858       }
859     }
860
861     return list;
862   }
863
864   /**
865    * Convenience, generates a unique constraint name. If the given constraint
866    * name is 'null' then a new one is created, otherwise the given default
867    * one is returned.
868    */

869   private static String JavaDoc makeUniqueConstraintName(String JavaDoc name,
870                                                  BigNumber unique_id) {
871     if (name == null) {
872       name = "_ANONYMOUS_CONSTRAINT_" + unique_id.toString();
873     }
874     return name;
875   }
876
877
878   /**
879    * Notifies this transaction that a database object with the given name has
880    * successfully been created.
881    */

882   void databaseObjectCreated(TableName table_name) {
883     // If this table name was dropped, then remove from the drop list
884
boolean dropped = dropped_database_objects.remove(table_name);
885     // If the above operation didn't remove a table name then add to the
886
// created database objects list.
887
if (!dropped) {
888       created_database_objects.add(table_name);
889     }
890   }
891
892   /**
893    * Notifies this transaction that a database object with the given name has
894    * successfully been dropped.
895    */

896   void databaseObjectDropped(TableName table_name) {
897     // If this table name was created, then remove from the create list
898
boolean created = created_database_objects.remove(table_name);
899     // If the above operation didn't remove a table name then add to the
900
// dropped database objects list.
901
if (!created) {
902       dropped_database_objects.add(table_name);
903     }
904   }
905
906   /**
907    * Returns the normalized list of database object names created in this
908    * transaction.
909    */

910   ArrayList JavaDoc getAllNamesCreated() {
911     return created_database_objects;
912   }
913
914   /**
915    * Returns the normalized list of database object names dropped in this
916    * transaction.
917    */

918   ArrayList JavaDoc getAllNamesDropped() {
919     return dropped_database_objects;
920   }
921
922
923   /**
924    * Create a new schema in this transaction. When the transaction is
925    * committed the schema will become globally accessable. Note that any
926    * security checks must be performed before this method is called.
927    * <p>
928    * NOTE: We must guarentee that the transaction be in exclusive mode before
929    * this method is called.
930    */

931   public void createSchema(String JavaDoc name, String JavaDoc type) {
932     TableName table_name = TableDataConglomerate.SCHEMA_INFO_TABLE;
933     MutableTableDataSource t = getTable(table_name);
934     SimpleTableQuery dt = new SimpleTableQuery(t);
935
936     try {
937       // Select entries where;
938
// sUSRSchemaInfo.name = name
939
if (!dt.existsSingle(1, name)) {
940         // Add the entry to the schema info table.
941
RowData rd = new RowData(t);
942         BigNumber unique_id = BigNumber.fromLong(nextUniqueID(table_name));
943         rd.setColumnDataFromObject(0, unique_id);
944         rd.setColumnDataFromObject(1, name);
945         rd.setColumnDataFromObject(2, type);
946         // Third (other) column is left as null
947
t.addRow(rd);
948       }
949       else {
950         throw new StatementException("Schema already exists: " + name);
951       }
952     }
953     finally {
954       dt.dispose();
955     }
956   }
957
958   /**
959    * Drops a schema from this transaction. When the transaction is committed
960    * the schema will be dropped perminently. Note that any security checks
961    * must be performed before this method is called.
962    * <p>
963    * NOTE: We must guarentee that the transaction be in exclusive mode before
964    * this method is called.
965    */

966   public void dropSchema(String JavaDoc name) {
967     TableName table_name = TableDataConglomerate.SCHEMA_INFO_TABLE;
968     MutableTableDataSource t = getTable(table_name);
969     SimpleTableQuery dt = new SimpleTableQuery(t);
970
971     // Drop a single entry from dt where column 1 = name
972
boolean b = dt.deleteSingle(1, name);
973     dt.dispose();
974     if (!b) {
975       throw new StatementException("Schema doesn't exists: " + name);
976     }
977   }
978
979   /**
980    * Returns true if the schema exists within this transaction.
981    */

982   public boolean schemaExists(String JavaDoc name) {
983     TableName table_name = TableDataConglomerate.SCHEMA_INFO_TABLE;
984     MutableTableDataSource t = getTable(table_name);
985     SimpleTableQuery dt = new SimpleTableQuery(t);
986
987     // Returns true if there's a single entry in dt where column 1 = name
988
boolean b = dt.existsSingle(1, name);
989     dt.dispose();
990     return b;
991   }
992
993   /**
994    * Resolves the case of the given schema name if the database is performing
995    * case insensitive identifier matching. Returns a SchemaDef object that
996    * identifiers the schema. Returns null if the schema name could not be
997    * resolved.
998    */

999   public SchemaDef resolveSchemaCase(String JavaDoc name, boolean ignore_case) {
1000    // The list of schema
1001
SimpleTableQuery dt = new SimpleTableQuery(
1002                         getTable(TableDataConglomerate.SCHEMA_INFO_TABLE));
1003
1004    try {
1005      RowEnumeration e = dt.rowEnumeration();
1006      if (ignore_case) {
1007        SchemaDef result = null;
1008        while (e.hasMoreRows()) {
1009          int row_index = e.nextRowIndex();
1010          String JavaDoc cur_name = dt.get(1, row_index).getObject().toString();
1011          if (name.equalsIgnoreCase(cur_name)) {
1012            if (result != null) {
1013              throw new StatementException(
1014                                     "Ambiguous schema name: '" + name + "'");
1015            }
1016            String JavaDoc type = dt.get(2, row_index).getObject().toString();
1017            result = new SchemaDef(cur_name, type);
1018          }
1019        }
1020        return result;
1021  
1022      }
1023      else { // if (!ignore_case)
1024
while (e.hasMoreRows()) {
1025          int row_index = e.nextRowIndex();
1026          String JavaDoc cur_name = dt.get(1, row_index).getObject().toString();
1027          if (name.equals(cur_name)) {
1028            String JavaDoc type = dt.get(2, row_index).getObject().toString();
1029            return new SchemaDef(cur_name, type);
1030          }
1031        }
1032        // Not found
1033
return null;
1034      }
1035    }
1036
1037    finally {
1038      dt.dispose();
1039    }
1040
1041  }
1042
1043  /**
1044   * Returns an array of SchemaDef objects for each schema currently setup in
1045   * the database.
1046   */

1047  public SchemaDef[] getSchemaList() {
1048    // The list of schema
1049
SimpleTableQuery dt = new SimpleTableQuery(
1050                         getTable(TableDataConglomerate.SCHEMA_INFO_TABLE));
1051    RowEnumeration e = dt.rowEnumeration();
1052    SchemaDef[] arr = new SchemaDef[dt.getRowCount()];
1053    int i = 0;
1054
1055    while (e.hasMoreRows()) {
1056      int row_index = e.nextRowIndex();
1057      String JavaDoc cur_name = dt.get(1, row_index).getObject().toString();
1058      String JavaDoc cur_type = dt.get(2, row_index).getObject().toString();
1059      arr[i] = new SchemaDef(cur_name, cur_type);
1060      ++i;
1061    }
1062
1063    dt.dispose();
1064    return arr;
1065  }
1066
1067
1068  /**
1069   * Sets a persistent variable of the database that becomes a committed
1070   * change once this transaction is committed. The variable can later be
1071   * retrieved with a call to the 'getPersistantVar' method. A persistant
1072   * var is created if it doesn't exist in the DatabaseVars table otherwise
1073   * it is overwritten.
1074   */

1075  public void setPersistentVar(String JavaDoc variable, String JavaDoc value) {
1076    TableName table_name = TableDataConglomerate.PERSISTENT_VAR_TABLE;
1077    MutableTableDataSource t = getTable(table_name);
1078    SimpleTableQuery dt = new SimpleTableQuery(t);
1079    dt.setVar(0, new Object JavaDoc[] { variable, value });
1080    dt.dispose();
1081  }
1082
1083  /**
1084   * Returns the value of the persistent variable with the given name or null
1085   * if it doesn't exist.
1086   */

1087  public String JavaDoc getPersistantVar(String JavaDoc variable) {
1088    TableName table_name = TableDataConglomerate.PERSISTENT_VAR_TABLE;
1089    MutableTableDataSource t = getTable(table_name);
1090    SimpleTableQuery dt = new SimpleTableQuery(t);
1091    String JavaDoc val = dt.getVar(1, 0, variable).toString();
1092    dt.dispose();
1093    return val;
1094  }
1095
1096  /**
1097   * Creates a new sequence generator with the given TableName and
1098   * initializes it with the given details. This does NOT check if the
1099   * given name clashes with an existing database object.
1100   */

1101  public void createSequenceGenerator(
1102               TableName name, long start_value, long increment_by,
1103               long min_value, long max_value, long cache, boolean cycle) {
1104    SequenceManager.createSequenceGenerator(this,
1105         name, start_value, increment_by, min_value, max_value, cache,
1106         cycle);
1107
1108    // Notify that this database object has been created
1109
databaseObjectCreated(name);
1110  }
1111
1112  /**
1113   * Drops an existing sequence generator with the given name.
1114   */

1115  public void dropSequenceGenerator(TableName name) {
1116    SequenceManager.dropSequenceGenerator(this, name);
1117    // Flush the sequence manager
1118
flushSequenceManager(name);
1119
1120    // Notify that this database object has been dropped
1121
databaseObjectDropped(name);
1122  }
1123
1124  /**
1125   * Adds a unique constraint to the database which becomes perminant when
1126   * the transaction is committed. Columns in a table that are defined as
1127   * unique are prevented from being duplicated by the engine.
1128   * <p>
1129   * NOTE: Security checks for adding constraints must be checked for at a
1130   * higher layer.
1131   * <p>
1132   * NOTE: We must guarentee that the transaction be in exclusive mode before
1133   * this method is called.
1134   */

1135  public void addUniqueConstraint(TableName table_name,
1136                    String JavaDoc[] cols, short deferred, String JavaDoc constraint_name) {
1137
1138    TableName tn1 = TableDataConglomerate.UNIQUE_INFO_TABLE;
1139    TableName tn2 = TableDataConglomerate.UNIQUE_COLS_TABLE;
1140    MutableTableDataSource t = getTable(tn1);
1141    MutableTableDataSource tcols = getTable(tn2);
1142
1143    try {
1144
1145      // Insert a value into UNIQUE_INFO_TABLE
1146
RowData rd = new RowData(t);
1147      BigNumber unique_id = BigNumber.fromLong(nextUniqueID(tn1));
1148      constraint_name = makeUniqueConstraintName(constraint_name, unique_id);
1149      rd.setColumnDataFromObject(0, unique_id);
1150      rd.setColumnDataFromObject(1, constraint_name);
1151      rd.setColumnDataFromObject(2, table_name.getSchema());
1152      rd.setColumnDataFromObject(3, table_name.getName());
1153      rd.setColumnDataFromObject(4, BigNumber.fromInt(deferred));
1154      t.addRow(rd);
1155
1156      // Insert the columns
1157
for (int i = 0; i < cols.length; ++i) {
1158        rd = new RowData(tcols);
1159        rd.setColumnDataFromObject(0, unique_id); // unique id
1160
rd.setColumnDataFromObject(1, cols[i]); // column name
1161
rd.setColumnDataFromObject(2, BigNumber.fromInt(i)); // sequence number
1162
tcols.addRow(rd);
1163      }
1164
1165    }
1166    catch (DatabaseConstraintViolationException e) {
1167      // Constraint violation when inserting the data. Check the type and
1168
// wrap around an appropriate error message.
1169
if (e.getErrorCode() ==
1170                DatabaseConstraintViolationException.UNIQUE_VIOLATION) {
1171        // This means we gave a constraint name that's already being used
1172
// for a primary key.
1173
throw new StatementException(
1174                        "Unique constraint name '" + constraint_name +
1175                        "' is already being used.");
1176      }
1177      throw e;
1178    }
1179
1180  }
1181
1182  /**
1183   * Adds a foreign key constraint to the database which becomes perminent
1184   * when the transaction is committed. A foreign key represents a referential
1185   * link from one table to another (may be the same table). The 'table_name',
1186   * 'cols' args represents the object to link from. The 'ref_table',
1187   * 'ref_cols' args represents the object to link to. The update rules are
1188   * for specifying cascading delete/update rules. The deferred arg is for
1189   * IMMEDIATE/DEFERRED checking.
1190   * <p>
1191   * NOTE: Security checks for adding constraints must be checked for at a
1192   * higher layer.
1193   * <p>
1194   * NOTE: We must guarentee that the transaction be in exclusive mode before
1195   * this method is called.
1196   */

1197  public void addForeignKeyConstraint(TableName table, String JavaDoc[] cols,
1198                                      TableName ref_table, String JavaDoc[] ref_cols,
1199                                      String JavaDoc delete_rule, String JavaDoc update_rule,
1200                                      short deferred, String JavaDoc constraint_name) {
1201    TableName tn1 = TableDataConglomerate.FOREIGN_INFO_TABLE;
1202    TableName tn2 = TableDataConglomerate.FOREIGN_COLS_TABLE;
1203    MutableTableDataSource t = getTable(tn1);
1204    MutableTableDataSource tcols = getTable(tn2);
1205
1206    try {
1207
1208      // If 'ref_columns' empty then set to primary key for referenced table,
1209
// ISSUE: What if primary key changes after the fact?
1210
if (ref_cols.length == 0) {
1211        ColumnGroup set = queryTablePrimaryKeyGroup(this, ref_table);
1212        if (set == null) {
1213          throw new StatementException(
1214            "No primary key defined for referenced table '" + ref_table + "'");
1215        }
1216        ref_cols = set.columns;
1217      }
1218
1219      if (cols.length != ref_cols.length) {
1220        throw new StatementException("Foreign key reference '" + table +
1221          "' -> '" + ref_table + "' does not have an equal number of " +
1222          "column terms.");
1223      }
1224
1225      // If delete or update rule is 'SET NULL' then check the foreign key
1226
// columns are not constrained as 'NOT NULL'
1227
if (delete_rule.equals("SET NULL") ||
1228          update_rule.equals("SET NULL")) {
1229        DataTableDef table_def = getDataTableDef(table);
1230        for (int i = 0; i < cols.length; ++i) {
1231          DataTableColumnDef column_def =
1232                        table_def.columnAt(table_def.findColumnName(cols[i]));
1233          if (column_def.isNotNull()) {
1234            throw new StatementException("Foreign key reference '" + table +
1235                   "' -> '" + ref_table + "' update or delete triggered " +
1236                   "action is SET NULL for columns that are constrained as " +
1237                   "NOT NULL.");
1238          }
1239        }
1240      }
1241      
1242      // Insert a value into FOREIGN_INFO_TABLE
1243
RowData rd = new RowData(t);
1244      BigNumber unique_id = BigNumber.fromLong(nextUniqueID(tn1));
1245      constraint_name = makeUniqueConstraintName(constraint_name, unique_id);
1246      rd.setColumnDataFromObject(0, unique_id);
1247      rd.setColumnDataFromObject(1, constraint_name);
1248      rd.setColumnDataFromObject(2, table.getSchema());
1249      rd.setColumnDataFromObject(3, table.getName());
1250      rd.setColumnDataFromObject(4, ref_table.getSchema());
1251      rd.setColumnDataFromObject(5, ref_table.getName());
1252      rd.setColumnDataFromObject(6, update_rule);
1253      rd.setColumnDataFromObject(7, delete_rule);
1254      rd.setColumnDataFromObject(8, BigNumber.fromInt(deferred));
1255      t.addRow(rd);
1256
1257      // Insert the columns
1258
for (int i = 0; i < cols.length; ++i) {
1259        rd = new RowData(tcols);
1260        rd.setColumnDataFromObject(0, unique_id); // unique id
1261
rd.setColumnDataFromObject(1, cols[i]); // column name
1262
rd.setColumnDataFromObject(2, ref_cols[i]); // ref column name
1263
rd.setColumnDataFromObject(3, BigNumber.fromInt(i)); // sequence number
1264
tcols.addRow(rd);
1265      }
1266
1267    }
1268    catch (DatabaseConstraintViolationException e) {
1269      // Constraint violation when inserting the data. Check the type and
1270
// wrap around an appropriate error message.
1271
if (e.getErrorCode() ==
1272                DatabaseConstraintViolationException.UNIQUE_VIOLATION) {
1273        // This means we gave a constraint name that's already being used
1274
// for a primary key.
1275
throw new StatementException("Foreign key constraint name '" +
1276                               constraint_name + "' is already being used.");
1277      }
1278      throw e;
1279    }
1280
1281  }
1282
1283  /**
1284   * Adds a primary key constraint that becomes perminent when the transaction
1285   * is committed. A primary key represents a set of columns in a table
1286   * that are constrained to be unique and can not be null. If the
1287   * constraint name parameter is 'null' a primary key constraint is created
1288   * with a unique constraint name.
1289   * <p>
1290   * NOTE: Security checks for adding constraints must be checked for at a
1291   * higher layer.
1292   * <p>
1293   * NOTE: We must guarentee that the transaction be in exclusive mode before
1294   * this method is called.
1295   */

1296  public void addPrimaryKeyConstraint(TableName table_name, String JavaDoc[] cols,
1297                                      short deferred, String JavaDoc constraint_name) {
1298
1299    TableName tn1 = TableDataConglomerate.PRIMARY_INFO_TABLE;
1300    TableName tn2 = TableDataConglomerate.PRIMARY_COLS_TABLE;
1301    MutableTableDataSource t = getTable(tn1);
1302    MutableTableDataSource tcols = getTable(tn2);
1303
1304    try {
1305
1306      // Insert a value into PRIMARY_INFO_TABLE
1307
RowData rd = new RowData(t);
1308      BigNumber unique_id = BigNumber.fromLong(nextUniqueID(tn1));
1309      constraint_name = makeUniqueConstraintName(constraint_name, unique_id);
1310      rd.setColumnDataFromObject(0, unique_id);
1311      rd.setColumnDataFromObject(1, constraint_name);
1312      rd.setColumnDataFromObject(2, table_name.getSchema());
1313      rd.setColumnDataFromObject(3, table_name.getName());
1314      rd.setColumnDataFromObject(4, BigNumber.fromInt(deferred));
1315      t.addRow(rd);
1316
1317      // Insert the columns
1318
for (int i = 0; i < cols.length; ++i) {
1319        rd = new RowData(tcols);
1320        rd.setColumnDataFromObject(0, unique_id); // unique id
1321
rd.setColumnDataFromObject(1, cols[i]); // column name
1322
rd.setColumnDataFromObject(2, BigNumber.fromInt(i)); // Sequence number
1323
tcols.addRow(rd);
1324      }
1325
1326    }
1327    catch (DatabaseConstraintViolationException e) {
1328      // Constraint violation when inserting the data. Check the type and
1329
// wrap around an appropriate error message.
1330
if (e.getErrorCode() ==
1331                DatabaseConstraintViolationException.UNIQUE_VIOLATION) {
1332        // This means we gave a constraint name that's already being used
1333
// for a primary key.
1334
throw new StatementException("Primary key constraint name '" +
1335                               constraint_name + "' is already being used.");
1336      }
1337      throw e;
1338    }
1339
1340  }
1341
1342  /**
1343   * Adds a check expression that becomes perminent when the transaction
1344   * is committed. A check expression is an expression that must evaluate
1345   * to true for all records added/updated in the database.
1346   * <p>
1347   * NOTE: Security checks for adding constraints must be checked for at a
1348   * higher layer.
1349   * <p>
1350   * NOTE: We must guarentee that the transaction be in exclusive mode before
1351   * this method is called.
1352   */

1353  public void addCheckConstraint(TableName table_name,
1354               Expression expression, short deferred, String JavaDoc constraint_name) {
1355
1356    TableName tn = TableDataConglomerate.CHECK_INFO_TABLE;
1357    MutableTableDataSource t = getTable(tn);
1358    int col_count = t.getDataTableDef().columnCount();
1359
1360    try {
1361
1362      // Insert check constraint data.
1363
BigNumber unique_id = BigNumber.fromLong(nextUniqueID(tn));
1364      constraint_name = makeUniqueConstraintName(constraint_name, unique_id);
1365      RowData rd = new RowData(t);
1366      rd.setColumnDataFromObject(0, unique_id);
1367      rd.setColumnDataFromObject(1, constraint_name);
1368      rd.setColumnDataFromObject(2, table_name.getSchema());
1369      rd.setColumnDataFromObject(3, table_name.getName());
1370      rd.setColumnDataFromObject(4, new String JavaDoc(expression.text()));
1371      rd.setColumnDataFromObject(5, BigNumber.fromInt(deferred));
1372      if (col_count > 6) {
1373        // Serialize the check expression
1374
ByteLongObject serialized_expression =
1375                                  ObjectTranslator.serialize(expression);
1376        rd.setColumnDataFromObject(6, serialized_expression);
1377      }
1378      t.addRow(rd);
1379
1380    }
1381    catch (DatabaseConstraintViolationException e) {
1382      // Constraint violation when inserting the data. Check the type and
1383
// wrap around an appropriate error message.
1384
if (e.getErrorCode() ==
1385                DatabaseConstraintViolationException.UNIQUE_VIOLATION) {
1386        // This means we gave a constraint name that's already being used.
1387
throw new StatementException("Check constraint name '" +
1388                               constraint_name + "' is already being used.");
1389      }
1390      throw e;
1391    }
1392
1393  }
1394
1395  /**
1396   * Drops all the constraints defined for the given table. This is a useful
1397   * function when dropping a table from the database.
1398   * <p>
1399   * NOTE: Security checks that the user can drop constraints must be checke at
1400   * a higher layer.
1401   * <p>
1402   * NOTE: We must guarentee that the transaction be in exclusive mode before
1403   * this method is called.
1404   */

1405  public void dropAllConstraintsForTable(TableName table_name) {
1406    ColumnGroup primary = queryTablePrimaryKeyGroup(this, table_name);
1407    ColumnGroup[] uniques = queryTableUniqueGroups(this, table_name);
1408    CheckExpression[] expressions =
1409                                 queryTableCheckExpressions(this, table_name);
1410    ColumnGroupReference[] refs =
1411                             queryTableForeignKeyReferences(this, table_name);
1412
1413    if (primary != null) {
1414      dropPrimaryKeyConstraintForTable(table_name, primary.name);
1415    }
1416    for (int i = 0; i < uniques.length; ++i) {
1417      dropUniqueConstraintForTable(table_name, uniques[i].name);
1418    }
1419    for (int i = 0; i < expressions.length; ++i) {
1420      dropCheckConstraintForTable(table_name, expressions[i].name);
1421    }
1422    for (int i = 0; i < refs.length; ++i) {
1423      dropForeignKeyReferenceConstraintForTable(table_name, refs[i].name);
1424    }
1425
1426  }
1427
1428  /**
1429   * Drops the named constraint from the transaction. Used when altering
1430   * table schema. Returns the number of constraints that were removed from
1431   * the system. If this method returns 0 then it indicates there is no
1432   * constraint with the given name in the table.
1433   * <p>
1434   * NOTE: Security checks that the user can drop constraints must be checke at
1435   * a higher layer.
1436   * <p>
1437   * NOTE: We must guarentee that the transaction be in exclusive mode before
1438   * this method is called.
1439   */

1440  public int dropNamedConstraint(TableName table_name,
1441                                 String JavaDoc constraint_name) {
1442
1443    int drop_count = 0;
1444    if (dropPrimaryKeyConstraintForTable(table_name, constraint_name)) {
1445      ++drop_count;
1446    }
1447    if (dropUniqueConstraintForTable(table_name, constraint_name)) {
1448      ++drop_count;
1449    }
1450    if (dropCheckConstraintForTable(table_name, constraint_name)) {
1451      ++drop_count;
1452    }
1453    if (dropForeignKeyReferenceConstraintForTable(table_name,
1454                                                  constraint_name)) {
1455      ++drop_count;
1456    }
1457    return drop_count;
1458  }
1459
1460  /**
1461   * Drops the primary key constraint for the given table. Used when altering
1462   * table schema. If 'constraint_name' is null this method will search for
1463   * the primary key of the table name. Returns true if the primary key
1464   * constraint was dropped (the constraint existed).
1465   * <p>
1466   * NOTE: Security checks that the user can drop constraints must be checke at
1467   * a higher layer.
1468   * <p>
1469   * NOTE: We must guarentee that the transaction be in exclusive mode before
1470   * this method is called.
1471   */

1472  public boolean dropPrimaryKeyConstraintForTable(
1473                            TableName table_name, String JavaDoc constraint_name) {
1474
1475    MutableTableDataSource t =
1476                         getTable(TableDataConglomerate.PRIMARY_INFO_TABLE);
1477    MutableTableDataSource t2 =
1478                         getTable(TableDataConglomerate.PRIMARY_COLS_TABLE);
1479    SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1480
SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1481

1482    try {
1483      IntegerVector data;
1484      if (constraint_name != null) {
1485        // Returns the list of indexes where column 1 = constraint name
1486
// and column 2 = schema name
1487
data = dt.selectIndexesEqual(1, constraint_name,
1488                                     2, table_name.getSchema());
1489      }
1490      else {
1491        // Returns the list of indexes where column 3 = table name
1492
// and column 2 = schema name
1493
data = dt.selectIndexesEqual(3, table_name.getName(),
1494                                     2, table_name.getSchema());
1495      }
1496
1497      if (data.size() > 1) {
1498        throw new Error JavaDoc("Assertion failed: multiple primary key for: " +
1499                        table_name);
1500      }
1501      else if (data.size() == 1) {
1502        int row_index = data.intAt(0);
1503        // The id
1504
TObject id = dt.get(0, row_index);
1505        // All columns with this id
1506
IntegerVector ivec = dtcols.selectIndexesEqual(0, id);
1507        // Delete from the table
1508
dtcols.deleteRows(ivec);
1509        dt.deleteRows(data);
1510        return true;
1511      }
1512      // data.size() must be 0 so no constraint was found to drop.
1513
return false;
1514
1515    }
1516    finally {
1517      dtcols.dispose();
1518      dt.dispose();
1519    }
1520
1521  }
1522
1523  /**
1524   * Drops a single named unique constraint from the given table. Returns
1525   * true if the unique constraint was dropped (the constraint existed).
1526   * <p>
1527   * NOTE: Security checks that the user can drop constraints must be checke at
1528   * a higher layer.
1529   * <p>
1530   * NOTE: We must guarentee that the transaction be in exclusive mode before
1531   * this method is called.
1532   */

1533  public boolean dropUniqueConstraintForTable(
1534                                   TableName table, String JavaDoc constraint_name) {
1535
1536    MutableTableDataSource t =
1537                         getTable(TableDataConglomerate.UNIQUE_INFO_TABLE);
1538    MutableTableDataSource t2 =
1539                         getTable(TableDataConglomerate.UNIQUE_COLS_TABLE);
1540    SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1541
SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1542

1543    try {
1544      // Returns the list of indexes where column 1 = constraint name
1545
// and column 2 = schema name
1546
IntegerVector data = dt.selectIndexesEqual(1, constraint_name,
1547                                                 2, table.getSchema());
1548
1549      if (data.size() > 1) {
1550        throw new Error JavaDoc("Assertion failed: multiple unique constraint name: " +
1551                        constraint_name);
1552      }
1553      else if (data.size() == 1) {
1554        int row_index = data.intAt(0);
1555        // The id
1556
TObject id = dt.get(0, row_index);
1557        // All columns with this id
1558
IntegerVector ivec = dtcols.selectIndexesEqual(0, id);
1559        // Delete from the table
1560
dtcols.deleteRows(ivec);
1561        dt.deleteRows(data);
1562        return true;
1563      }
1564      // data.size() == 0 so the constraint wasn't found
1565
return false;
1566    }
1567    finally {
1568      dtcols.dispose();
1569      dt.dispose();
1570    }
1571
1572  }
1573
1574  /**
1575   * Drops a single named check constraint from the given table. Returns true
1576   * if the check constraint was dropped (the constraint existed).
1577   * <p>
1578   * NOTE: Security checks that the user can drop constraints must be checke at
1579   * a higher layer.
1580   * <p>
1581   * NOTE: We must guarentee that the transaction be in exclusive mode before
1582   * this method is called.
1583   */

1584  public boolean dropCheckConstraintForTable(
1585                                   TableName table, String JavaDoc constraint_name) {
1586
1587    MutableTableDataSource t =
1588                         getTable(TableDataConglomerate.CHECK_INFO_TABLE);
1589    SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1590

1591    try {
1592      // Returns the list of indexes where column 1 = constraint name
1593
// and column 2 = schema name
1594
IntegerVector data = dt.selectIndexesEqual(1, constraint_name,
1595                                                 2, table.getSchema());
1596
1597      if (data.size() > 1) {
1598        throw new Error JavaDoc("Assertion failed: multiple check constraint name: " +
1599                        constraint_name);
1600      }
1601      else if (data.size() == 1) {
1602        // Delete the check constraint
1603
dt.deleteRows(data);
1604        return true;
1605      }
1606      // data.size() == 0 so the constraint wasn't found
1607
return false;
1608    }
1609    finally {
1610      dt.dispose();
1611    }
1612
1613  }
1614
1615  /**
1616   * Drops a single named foreign key reference from the given table. Returns
1617   * true if the foreign key reference constraint was dropped (the constraint
1618   * existed).
1619   * <p>
1620   * NOTE: Security checks that the user can drop constraints must be checke at
1621   * a higher layer.
1622   * <p>
1623   * NOTE: We must guarentee that the transaction be in exclusive mode before
1624   * this method is called.
1625   */

1626  public boolean dropForeignKeyReferenceConstraintForTable(
1627                                   TableName table, String JavaDoc constraint_name) {
1628
1629    MutableTableDataSource t =
1630                         getTable(TableDataConglomerate.FOREIGN_INFO_TABLE);
1631    MutableTableDataSource t2 =
1632                         getTable(TableDataConglomerate.FOREIGN_COLS_TABLE);
1633    SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1634
SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1635

1636    try {
1637      // Returns the list of indexes where column 1 = constraint name
1638
// and column 2 = schema name
1639
IntegerVector data = dt.selectIndexesEqual(1, constraint_name,
1640                                                 2, table.getSchema());
1641
1642      if (data.size() > 1) {
1643        throw new Error JavaDoc("Assertion failed: multiple foreign key constraint " +
1644                        "name: " + constraint_name);
1645      }
1646      else if (data.size() == 1) {
1647        int row_index = data.intAt(0);
1648        // The id
1649
TObject id = dt.get(0, row_index);
1650        // All columns with this id
1651
IntegerVector ivec = dtcols.selectIndexesEqual(0, id);
1652        // Delete from the table
1653
dtcols.deleteRows(ivec);
1654        dt.deleteRows(data);
1655        return true;
1656      }
1657      // data.size() == 0 so the constraint wasn't found
1658
return false;
1659    }
1660    finally {
1661      dtcols.dispose();
1662      dt.dispose();
1663    }
1664
1665  }
1666
1667  /**
1668   * Returns the list of tables (as a TableName array) that are dependant
1669   * on the data in the given table to maintain referential consistancy. The
1670   * list includes the tables referenced as foreign keys, and the tables
1671   * that reference the table as a foreign key.
1672   * <p>
1673   * This is a useful query for determining ahead of time the tables that
1674   * require a read lock when inserting/updating a table. A table will require
1675   * a read lock if the operation needs to query it for potential referential
1676   * integrity violations.
1677   */

1678  public static TableName[] queryTablesRelationallyLinkedTo(
1679                       SimpleTransaction transaction, TableName table) {
1680    ArrayList JavaDoc list = new ArrayList JavaDoc();
1681    ColumnGroupReference[] refs =
1682                            queryTableForeignKeyReferences(transaction, table);
1683    for (int i = 0; i < refs.length; ++i) {
1684      TableName tname = refs[i].ref_table_name;
1685      if (!list.contains(tname)) {
1686        list.add(tname);
1687      }
1688    }
1689    refs = queryTableImportedForeignKeyReferences(transaction, table);
1690    for (int i = 0; i < refs.length; ++i) {
1691      TableName tname = refs[i].key_table_name;
1692      if (!list.contains(tname)) {
1693        list.add(tname);
1694      }
1695    }
1696    return (TableName[]) list.toArray(new TableName[list.size()]);
1697  }
1698
1699  /**
1700   * Returns a set of unique groups that are constrained to be unique for
1701   * the given table in this transaction. For example, if columns ('name')
1702   * and ('number', 'document_rev') are defined as unique, this will return
1703   * an array of two groups that represent unique columns in the given
1704   * table.
1705   */

1706  public static ColumnGroup[] queryTableUniqueGroups(
1707                  SimpleTransaction transaction, TableName table_name) {
1708    TableDataSource t =
1709      transaction.getTableDataSource(TableDataConglomerate.UNIQUE_INFO_TABLE);
1710    TableDataSource t2 =
1711      transaction.getTableDataSource(TableDataConglomerate.UNIQUE_COLS_TABLE);
1712    SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1713
SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1714

1715    ColumnGroup[] groups;
1716    try {
1717      // Returns the list indexes where column 3 = table name
1718
// and column 2 = schema name
1719
IntegerVector data = dt.selectIndexesEqual(3, table_name.getName(),
1720                                                 2, table_name.getSchema());
1721      groups = new ColumnGroup[data.size()];
1722
1723      for (int i = 0; i < data.size(); ++i) {
1724        TObject id = dt.get(0, data.intAt(i));
1725
1726        // Select all records with equal id
1727
IntegerVector cols = dtcols.selectIndexesEqual(0, id);
1728
1729        // Put into a group.
1730
ColumnGroup group = new ColumnGroup();
1731        // constraint name
1732
group.name = dt.get(1, data.intAt(i)).getObject().toString();
1733        group.columns = toColumns(dtcols, cols); // the list of columns
1734
group.deferred = ((BigNumber) dt.get(4,
1735                                     data.intAt(i)).getObject()).shortValue();
1736        groups[i] = group;
1737      }
1738    }
1739    finally {
1740      dt.dispose();
1741      dtcols.dispose();
1742    }
1743
1744    return groups;
1745  }
1746
1747  /**
1748   * Returns a set of primary key groups that are constrained to be unique
1749   * for the given table in this transaction (there can be only 1 primary
1750   * key defined for a table). Returns null if there is no primary key
1751   * defined for the table.
1752   */

1753  public static ColumnGroup queryTablePrimaryKeyGroup(
1754                  SimpleTransaction transaction, TableName table_name) {
1755    TableDataSource t =
1756      transaction.getTableDataSource(TableDataConglomerate.PRIMARY_INFO_TABLE);
1757    TableDataSource t2 =
1758      transaction.getTableDataSource(TableDataConglomerate.PRIMARY_COLS_TABLE);
1759    SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1760
SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1761

1762    try {
1763      // Returns the list indexes where column 3 = table name
1764
// and column 2 = schema name
1765
IntegerVector data = dt.selectIndexesEqual(3, table_name.getName(),
1766                                                 2, table_name.getSchema());
1767
1768      if (data.size() > 1) {
1769        throw new Error JavaDoc("Assertion failed: multiple primary key for: " +
1770                        table_name);
1771      }
1772      else if (data.size() == 1) {
1773        int row_index = data.intAt(0);
1774        // The id
1775
TObject id = dt.get(0, row_index);
1776        // All columns with this id
1777
IntegerVector ivec = dtcols.selectIndexesEqual(0, id);
1778        // Make it in to a columns object
1779
ColumnGroup group = new ColumnGroup();
1780        group.name = dt.get(1, row_index).getObject().toString();
1781        group.columns = toColumns(dtcols, ivec);
1782        group.deferred = ((BigNumber) dt.get(4,
1783                                        row_index).getObject()).shortValue();
1784        return group;
1785      }
1786      else {
1787        return null;
1788      }
1789    }
1790    finally {
1791      dt.dispose();
1792      dtcols.dispose();
1793    }
1794
1795  }
1796
1797  /**
1798   * Returns a set of check expressions that are constrained over all new
1799   * columns added to the given table in this transaction. For example,
1800   * we may want a column called 'serial_number' to be constrained as
1801   * CHECK serial_number LIKE '___-________-___'.
1802   */

1803  public static CheckExpression[] queryTableCheckExpressions(
1804                  SimpleTransaction transaction, TableName table_name) {
1805    TableDataSource t =
1806        transaction.getTableDataSource(TableDataConglomerate.CHECK_INFO_TABLE);
1807    SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1808

1809    CheckExpression[] checks;
1810    try {
1811      // Returns the list indexes where column 3 = table name
1812
// and column 2 = schema name
1813
IntegerVector data = dt.selectIndexesEqual(3, table_name.getName(),
1814                                                 2, table_name.getSchema());
1815      checks = new CheckExpression[data.size()];
1816
1817      for (int i = 0; i < checks.length; ++i) {
1818        int row_index = data.intAt(i);
1819
1820        CheckExpression check = new CheckExpression();
1821        check.name = dt.get(1, row_index).getObject().toString();
1822        check.deferred = ((BigNumber) dt.get(5,
1823                                          row_index).getObject()).shortValue();
1824        // Is the deserialized version available?
1825
if (t.getDataTableDef().columnCount() > 6) {
1826          ByteLongObject sexp =
1827                             (ByteLongObject) dt.get(6, row_index).getObject();
1828          if (sexp != null) {
1829            try {
1830              // Deserialize the expression
1831
check.expression =
1832                            (Expression) ObjectTranslator.deserialize(sexp);
1833            }
1834            catch (Throwable JavaDoc e) {
1835              // We weren't able to deserialize the expression so report the
1836
// error to the log
1837
transaction.Debug().write(Lvl.WARNING, Transaction.class,
1838                    "Unable to deserialize the check expression. " +
1839                    "The error is: " + e.getMessage());
1840              transaction.Debug().write(Lvl.WARNING, Transaction.class,
1841                    "Parsing the check expression instead.");
1842              check.expression = null;
1843            }
1844          }
1845        }
1846        // Otherwise we need to parse it from the string
1847
if (check.expression == null) {
1848          Expression exp = Expression.parse(
1849                                 dt.get(4, row_index).getObject().toString());
1850          check.expression = exp;
1851        }
1852        checks[i] = check;
1853      }
1854
1855    }
1856    finally {
1857      dt.dispose();
1858    }
1859
1860    return checks;
1861  }
1862
1863  /**
1864   * Returns an array of column references in the given table that represent
1865   * foreign key references. For example, say a foreign reference has been
1866   * set up in the given table as follows;<p><pre>
1867   * FOREIGN KEY (customer_id) REFERENCES Customer (id)
1868   * </pre><p>
1869   * This method will return the column group reference
1870   * Order(customer_id) -> Customer(id).
1871   * <p>
1872   * This method is used to check that a foreign key reference actually points
1873   * to a valid record in the referenced table as expected.
1874   */

1875  public static ColumnGroupReference[] queryTableForeignKeyReferences(
1876                  SimpleTransaction transaction, TableName table_name) {
1877
1878    TableDataSource t =
1879      transaction.getTableDataSource(TableDataConglomerate.FOREIGN_INFO_TABLE);
1880    TableDataSource t2 =
1881      transaction.getTableDataSource(TableDataConglomerate.FOREIGN_COLS_TABLE);
1882    SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1883
SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1884

1885    ColumnGroupReference[] groups;
1886    try {
1887      // Returns the list indexes where column 3 = table name
1888
// and column 2 = schema name
1889
IntegerVector data = dt.selectIndexesEqual(3, table_name.getName(),
1890                                                 2, table_name.getSchema());
1891      groups = new ColumnGroupReference[data.size()];
1892
1893      for (int i = 0; i < data.size(); ++i) {
1894        int row_index = data.intAt(i);
1895
1896        // The foreign key id
1897
TObject id = dt.get(0, row_index);
1898
1899        // The referenced table
1900
TableName ref_table_name = new TableName(
1901                   dt.get(4, row_index).getObject().toString(),
1902                   dt.get(5, row_index).getObject().toString());
1903
1904        // Select all records with equal id
1905
IntegerVector cols = dtcols.selectIndexesEqual(0, id);
1906
1907        // Put into a group.
1908
ColumnGroupReference group = new ColumnGroupReference();
1909        // constraint name
1910
group.name = dt.get(1, row_index).getObject().toString();
1911        group.key_table_name = table_name;
1912        group.ref_table_name = ref_table_name;
1913        group.update_rule = dt.get(6, row_index).getObject().toString();
1914        group.delete_rule = dt.get(7, row_index).getObject().toString();
1915        group.deferred = ((BigNumber) dt.get(8,
1916                                        row_index).getObject()).shortValue();
1917
1918        int cols_size = cols.size();
1919        String JavaDoc[] key_cols = new String JavaDoc[cols_size];
1920        String JavaDoc[] ref_cols = new String JavaDoc[cols_size];
1921        for (int n = 0; n < cols_size; ++n) {
1922          for (int p = 0; p < cols_size; ++p) {
1923            int cols_index = cols.intAt(p);
1924            if (((BigNumber) dtcols.get(3,
1925                                  cols_index).getObject()).intValue() == n) {
1926              key_cols[n] = dtcols.get(1, cols_index).getObject().toString();
1927              ref_cols[n] = dtcols.get(2, cols_index).getObject().toString();
1928              break;
1929            }
1930          }
1931        }
1932        group.key_columns = key_cols;
1933        group.ref_columns = ref_cols;
1934
1935        groups[i] = group;
1936      }
1937    }
1938    finally {
1939      dt.dispose();
1940      dtcols.dispose();
1941    }
1942
1943    return groups;
1944  }
1945
1946  /**
1947   * Returns an array of column references in the given table that represent
1948   * foreign key references that reference columns in the given table. This
1949   * is a reverse mapping of the 'queryTableForeignKeyReferences' method. For
1950   * example, say a foreign reference has been set up in any table as follows;
1951   * <p><pre>
1952   * [ In table Order ]
1953   * FOREIGN KEY (customer_id) REFERENCE Customer (id)
1954   * </pre><p>
1955   * And the table name we are querying is 'Customer' then this method will
1956   * return the column group reference
1957   * Order(customer_id) -> Customer(id).
1958   * <p>
1959   * This method is used to check that a reference isn't broken when we remove
1960   * a record (for example, removing a Customer that has references to it will
1961   * break integrity).
1962   */

1963  public static ColumnGroupReference[] queryTableImportedForeignKeyReferences(
1964              SimpleTransaction transaction, TableName ref_table_name) {
1965
1966    TableDataSource t =
1967      transaction.getTableDataSource(TableDataConglomerate.FOREIGN_INFO_TABLE);
1968    TableDataSource t2 =
1969      transaction.getTableDataSource(TableDataConglomerate.FOREIGN_COLS_TABLE);
1970    SimpleTableQuery dt = new SimpleTableQuery(t); // The info table
1971
SimpleTableQuery dtcols = new SimpleTableQuery(t2); // The columns
1972

1973    ColumnGroupReference[] groups;
1974    try {
1975      // Returns the list indexes where column 5 = ref table name
1976
// and column 4 = ref schema name
1977
IntegerVector data = dt.selectIndexesEqual(5,ref_table_name.getName(),
1978                                                 4,ref_table_name.getSchema());
1979      groups = new ColumnGroupReference[data.size()];
1980
1981      for (int i = 0; i < data.size(); ++i) {
1982        int row_index = data.intAt(i);
1983
1984        // The foreign key id
1985
TObject id = dt.get(0, row_index);
1986
1987        // The referencee table
1988
TableName table_name = new TableName(
1989              dt.get(2, row_index).getObject().toString(),
1990              dt.get(3, row_index).getObject().toString());
1991
1992        // Select all records with equal id
1993
IntegerVector cols = dtcols.selectIndexesEqual(0, id);
1994
1995        // Put into a group.
1996
ColumnGroupReference group = new ColumnGroupReference();
1997        // constraint name
1998
group.name = dt.get(1, row_index).getObject().toString();
1999        group.key_table_name = table_name;
2000        group.ref_table_name = ref_table_name;
2001        group.update_rule = dt.get(6, row_index).getObject().toString();
2002        group.delete_rule = dt.get(7, row_index).getObject().toString();
2003        group.deferred = ((BigNumber) dt.get(8,
2004                                        row_index).getObject()).shortValue();
2005
2006        int cols_size = cols.size();
2007        String JavaDoc[] key_cols = new String JavaDoc[cols_size];
2008        String JavaDoc[] ref_cols = new String JavaDoc[cols_size];
2009        for (int n = 0; n < cols_size; ++n) {
2010          for (int p = 0; p < cols_size; ++p) {
2011            int cols_index = cols.intAt(p);
2012            if (((BigNumber) dtcols.get(3,
2013                                   cols_index).getObject()).intValue() == n) {
2014              key_cols[n] = dtcols.get(1, cols_index).getObject().toString();
2015              ref_cols[n] = dtcols.get(2, cols_index).getObject().toString();
2016              break;
2017            }
2018          }
2019        }
2020        group.key_columns = key_cols;
2021        group.ref_columns = ref_cols;
2022
2023        groups[i] = group;
2024      }
2025    }
2026    finally {
2027      dt.dispose();
2028      dtcols.dispose();
2029    }
2030
2031    return groups;
2032  }
2033
2034
2035
2036
2037
2038
2039
2040  // ----- Transaction close operations -----
2041

2042  /**
2043   * Closes and marks a transaction as committed. Any changes made by this
2044   * transaction are seen by all transactions created after this method
2045   * returns.
2046   * <p>
2047   * This method will fail under the following circumstances:
2048   * <ol>
2049   * <li> There are any rows deleted in this transaction that were deleted
2050   * by another successfully committed transaction.
2051   * <li> There were rows added in another committed transaction that would
2052   * change the result of the search clauses committed by this transaction.
2053   * </ol>
2054   * The first check is not too difficult to check for. The second is very
2055   * difficult however we need it to ensure TRANSACTION_SERIALIZABLE isolation
2056   * is enforced. We may have to simplify this by throwing a transaction
2057   * exception if the table has had any changes made to it during this
2058   * transaction.
2059   * <p>
2060   * This should only be called under an exclusive lock on the connection.
2061   */

2062  public void closeAndCommit() throws TransactionException {
2063
2064    if (!closed) {
2065      try {
2066        closed = true;
2067        // Get the conglomerate to do this commit.
2068
conglomerate.processCommit(this, getVisibleTables(),
2069                                   selected_from_tables,
2070                                   touched_tables, journal);
2071      }
2072      finally {
2073        cleanup();
2074      }
2075    }
2076
2077  }
2078
2079  /**
2080   * Closes and rolls back a transaction as if the commands the transaction ran
2081   * never happened. This will not throw a transaction exception.
2082   * <p>
2083   * This should only be called under an exclusive lock on the connection.
2084   */

2085  public void closeAndRollback() {
2086
2087    if (!closed) {
2088      try {
2089        closed = true;
2090        // Notify the conglomerate that this transaction has closed.
2091
conglomerate.processRollback(this, touched_tables, journal);
2092      }
2093      finally {
2094        cleanup();
2095      }
2096    }
2097
2098  }
2099
2100  /**
2101   * Cleans up this transaction.
2102   */

2103  private void cleanup() {
2104    getSystem().stats().decrement("Transaction.count");
2105    // Dispose of all the IndexSet objects created by this transaction.
2106
disposeAllIndices();
2107
2108    // Dispose all the table we touched
2109
try {
2110      for (int i = 0; i < touched_tables.size(); ++i) {
2111        MutableTableDataSource source =
2112                               (MutableTableDataSource) touched_tables.get(i);
2113        source.dispose();
2114      }
2115    }
2116    catch (Throwable JavaDoc e) {
2117      Debug().writeException(e);
2118    }
2119
2120    getSystem().stats().increment("Transaction.cleanup");
2121    conglomerate = null;
2122    touched_tables = null;
2123    journal = null;
2124  }
2125
2126  /**
2127   * Disposes this transaction without rolling back or committing the changes.
2128   * Care should be taken when using this - it must only be used for simple
2129   * transactions that are short lived and have not modified the database.
2130   */

2131  void dispose() {
2132    if (!isReadOnly()) {
2133      throw new RuntimeException JavaDoc(
2134          "Assertion failed - tried to dispose a non read-only transaction.");
2135    }
2136    if (!closed) {
2137      closed = true;
2138      cleanup();
2139    }
2140  }
2141  
2142  /**
2143   * Finalize, we should close the transaction.
2144   */

2145  public void finalize() throws Throwable JavaDoc {
2146    super.finalize();
2147    if (!closed) {
2148      Debug().write(Lvl.ERROR, this, "Transaction not closed!");
2149      closeAndRollback();
2150    }
2151  }
2152
2153
2154
2155  // ---------- Transaction inner classes ----------
2156

2157  /**
2158   * A list of DataTableDef system table definitions for tables internal to
2159   * the transaction.
2160   */

2161  private final static DataTableDef[] INTERNAL_DEF_LIST;
2162
2163  static {
2164    INTERNAL_DEF_LIST = new DataTableDef[3];
2165    INTERNAL_DEF_LIST[0] = GTTableColumnsDataSource.DEF_DATA_TABLE_DEF;
2166    INTERNAL_DEF_LIST[1] = GTTableInfoDataSource.DEF_DATA_TABLE_DEF;
2167    INTERNAL_DEF_LIST[2] = GTProductDataSource.DEF_DATA_TABLE_DEF;
2168  }
2169
2170  /**
2171   * A static internal table info for internal tables to the transaction.
2172   * This implementation includes all the dynamically generated system tables
2173   * that are tied to information in a transaction.
2174   */

2175  private class TransactionInternalTables extends AbstractInternalTableInfo {
2176
2177    /**
2178     * Constructor.
2179     */

2180    public TransactionInternalTables() {
2181      super("SYSTEM TABLE", INTERNAL_DEF_LIST);
2182    }
2183
2184    // ---------- Implemented ----------
2185

2186    public MutableTableDataSource createInternalTable(int index) {
2187      if (index == 0) {
2188        return new GTTableColumnsDataSource(Transaction.this).init();
2189      }
2190      else if (index == 1) {
2191        return new GTTableInfoDataSource(Transaction.this).init();
2192      }
2193      else if (index == 2) {
2194        return new GTProductDataSource(Transaction.this).init();
2195      }
2196      else {
2197        throw new RuntimeException JavaDoc();
2198      }
2199    }
2200
2201  }
2202
2203  /**
2204   * A group of columns as used by the constraint system. A ColumnGroup is
2205   * a simple list of columns in a table.
2206   */

2207  public static class ColumnGroup {
2208
2209    /**
2210     * The name of the group (the constraint name).
2211     */

2212    public String JavaDoc name;
2213
2214    /**
2215     * The list of columns that make up the group.
2216     */

2217    public String JavaDoc[] columns;
2218
2219    /**
2220     * Whether this is deferred or initially immediate.
2221     */

2222    public short deferred;
2223
2224  }
2225
2226  /**
2227   * Represents a constraint expression to check.
2228   */

2229  public static class CheckExpression {
2230
2231    /**
2232     * The name of the check expression (the constraint name).
2233     */

2234    public String JavaDoc name;
2235
2236    /**
2237     * The expression to check.
2238     */

2239    public Expression expression;
2240
2241    /**
2242     * Whether this is deferred or initially immediate.
2243     */

2244    public short deferred;
2245
2246  }
2247
2248  /**
2249   * Represents a reference from a group of columns in one table to a group of
2250   * columns in another table. The is used to represent a foreign key
2251   * reference.
2252   */

2253  public static class ColumnGroupReference {
2254
2255    /**
2256     * The name of the group (the constraint name).
2257     */

2258    public String JavaDoc name;
2259
2260    /**
2261     * The key table name.
2262     */

2263    public TableName key_table_name;
2264
2265    /**
2266     * The list of columns that make up the key.
2267     */

2268    public String JavaDoc[] key_columns;
2269
2270    /**
2271     * The referenced table name.
2272     */

2273    public TableName ref_table_name;
2274
2275    /**
2276     * The list of columns that make up the referenced group.
2277     */

2278    public String JavaDoc[] ref_columns;
2279
2280    /**
2281     * The update rule.
2282     */

2283    public String JavaDoc update_rule;
2284
2285    /**
2286     * The delete rule.
2287     */

2288    public String JavaDoc delete_rule;
2289
2290    /**
2291     * Whether this is deferred or initially immediate.
2292     */

2293    public short deferred;
2294
2295  }
2296
2297}
2298
Popular Tags