KickJava   Java API By Example, From Geeks To Geeks.

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


1 /**
2  * com.mckoi.database.DatabaseConnection 21 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.Cache;
29 import com.mckoi.database.global.ByteLongObject;
30 import com.mckoi.database.global.Ref;
31 import com.mckoi.database.jdbc.SQLQuery;
32 import java.util.HashMap JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.math.BigDecimal JavaDoc;
35
36 /**
37  * An object that represents a connection to a Database. This object handles
38  * all transactional queries and modifications to the database.
39  *
40  * @author Tobias Downer
41  */

42
43 public class DatabaseConnection implements TriggerListener {
44
45   /**
46    * The User that this connection has been made by.
47    */

48   private User user;
49
50   /**
51    * The Database object that this connection is on.
52    */

53   private Database database;
54
55   /**
56    * The DebugLogger object that we can use to log messages to.
57    */

58   private DebugLogger logger;
59
60   /**
61    * A loop-back object that is managing this connection. This typically is
62    * the session protocol. This is notified of all connection events, such as
63    * triggers.
64    */

65   private CallBack call_back;
66
67   /**
68    * The locking mechanism within this connection.
69    */

70   private LockingMechanism locking_mechanism;
71
72   /**
73    * The TableDataConglomerate object that is used for transactional access
74    * to the data.
75    */

76   private TableDataConglomerate conglomerate;
77
78   /**
79    * The current Transaction that this connection is operating within.
80    */

81   private Transaction transaction;
82
83   /**
84    * The current java.sql.Connection object that can be used to access the
85    * transaction internally.
86    */

87   private java.sql.Connection JavaDoc jdbc_connection;
88
89   /**
90    * A HashMap of DataTable objects that have been created within this
91    * connection.
92    */

93   private HashMap JavaDoc tables_cache;
94
95   /**
96    * A buffer of triggers. This contains triggers that can't fire until
97    * the current transaction has closed. These triggers were generated by
98    * external actions outside of the context of this transaction.
99    */

100   private ArrayList JavaDoc trigger_event_buffer;
101
102   /**
103    * A list of triggers that are fired by actions taken on tables in this
104    * transaction. When the transaction is successfully committed, these
105    * trigger events need to be propogated to other connections in the database
106    * listening for trigger events on the triggered objects.
107    */

108   private ArrayList JavaDoc trigger_event_list;
109   
110   /**
111    * If this is true then the database connection is in 'auto-commit' mode.
112    * This implies a COMMIT instruction is executed after every complete
113    * statement in the language grammar. By default this is true.
114    */

115   private boolean auto_commit;
116
117   /**
118    * The current transaction isolation level this connect is operating under.
119    * 1 = READ UNCOMMITTED, 2 = READ COMMITTED, 3 = REPEATABLE READ,
120    * 4 = SERIALIZABLE.
121    */

122   private int transaction_isolation;
123
124   /**
125    * A flag which if set to true, will not allow 'commit' or 'rollback'
126    * commands on the transaction to occur and therefore prevent any open
127    * transaction from closing. This is useful for restricting the ability
128    * of a stored procedure to close.
129    */

130   private boolean close_transaction_disabled;
131   
132   /**
133    * The name of the schema that this connection is currently in. If the
134    * schema is "" then this connection is in the default schema (effectively
135    * no schema).
136    */

137   private String JavaDoc current_schema;
138
139   /**
140    * The GrantManager object for this connection.
141    */

142   private GrantManager grant_manager;
143
144   /**
145    * The procedure manager object for this connection.
146    */

147   private ProcedureManager procedure_manager;
148
149   /**
150    * The connection trigger manager that handles actions that cause triggers
151    * to fire on this connection.
152    */

153   private ConnectionTriggerManager connection_trigger_manager;
154
155   /**
156    * The connection view manager that handles view information through this
157    * connection.
158    */

159   private ViewManager view_manager;
160   
161   /**
162    * The list of all TableBackedCache objects that have been attached to this
163    * DatabaseConnection and are to be notified of transaction start/end
164    * events.
165    */

166   private ArrayList JavaDoc table_backed_cache_list;
167
168   /**
169    * A local member that represents the static list of internal tables
170    * that represent connection specific properties such as username,
171    * connection, statistics, etc.
172    */

173   private ConnectionInternalTableInfo connection_internal_table_info;
174   
175   // ----- Local flags -----
176

177   /**
178    * True if transactions through this connection generate an error when
179    * there is a dirty select on a table.
180    */

181   private boolean error_on_dirty_select;
182
183   /**
184    * True if this connection resolves identifiers case insensitive.
185    */

186   private boolean case_insensitive_identifiers;
187
188   // ----- OLD and NEW table information for triggers -----
189

190   /**
191    * A local member that represents the OLD and NEW system tables that
192    * represent the OLD and NEW data in a triggered action.
193    */

194   private OldAndNewInternalTableInfo old_new_table_info;
195
196   /**
197    * The current state of the OLD and NEW system tables including any cached
198    * information about the tables.
199    */

200   private OldNewTableState current_old_new_state = new OldNewTableState();
201
202
203
204   /**
205    * (package protected) Constructs the connection.
206    */

207   DatabaseConnection(Database database, User user, CallBack call_back) {
208     this.database = database;
209     this.user = user;
210     this.logger = database.Debug();
211     this.call_back = call_back;
212     this.conglomerate = database.getConglomerate();
213     this.locking_mechanism = new LockingMechanism(Debug());
214     this.trigger_event_buffer = new ArrayList JavaDoc();
215     this.trigger_event_list = new ArrayList JavaDoc();
216     tables_cache = new HashMap JavaDoc();
217     auto_commit = true;
218
219     current_schema = Database.DEFAULT_SCHEMA;
220     this.close_transaction_disabled = false;
221
222     this.table_backed_cache_list = new ArrayList JavaDoc();
223
224     connection_internal_table_info = new ConnectionInternalTableInfo();
225     old_new_table_info = new OldAndNewInternalTableInfo();
226
227     error_on_dirty_select =
228                           database.getSystem().transactionErrorOnDirtySelect();
229     case_insensitive_identifiers = database.getSystem().ignoreIdentifierCase();
230
231   }
232
233   /**
234    * Initializes this DatabaseConnection (possibly by initializing state from
235    * the database).
236    */

237   void init() {
238     // Create the grant manager for this connection.
239
grant_manager = new GrantManager(this);
240     // Create the procedure manager for this connection.
241
procedure_manager = new ProcedureManager(this);
242     // Create the connection trigger manager object
243
connection_trigger_manager = new ConnectionTriggerManager(this);
244     // Create the view manager
245
view_manager = new ViewManager(this);
246   }
247   
248   /**
249    * Returns the transaction. If 'transaction' is null then it opens a
250    * new transaction within the conglomerate.
251    */

252   private Transaction getTransaction() {
253     synchronized (this) {
254       if (transaction == null) {
255         transaction = conglomerate.createTransaction();
256         transaction.setErrorOnDirtySelect(error_on_dirty_select);
257         // Internal tables (connection statistics, etc)
258
transaction.addInternalTableInfo(connection_internal_table_info);
259         // OLD and NEW system tables (if applicable)
260
transaction.addInternalTableInfo(old_new_table_info);
261         // Model views as tables (obviously)
262
transaction.addInternalTableInfo(
263               ViewManager.createInternalTableInfo(view_manager, transaction));
264         // Model procedures as tables
265
transaction.addInternalTableInfo(
266               ProcedureManager.createInternalTableInfo(transaction));
267         // Model sequences as tables
268
transaction.addInternalTableInfo(
269               SequenceManager.createInternalTableInfo(transaction));
270         // Model triggers as tables
271
transaction.addInternalTableInfo(
272               ConnectionTriggerManager.createInternalTableInfo(transaction));
273
274         // Notify any table backed caches that this transaction has started.
275
int sz = table_backed_cache_list.size();
276         for (int i = 0; i < sz; ++i) {
277           TableBackedCache cache =
278                            (TableBackedCache) table_backed_cache_list.get(i);
279           cache.transactionStarted();
280         }
281
282       }
283     }
284     return transaction;
285   }
286
287   /**
288    * Returns a freshly deserialized QueryPlanNode object for the given view
289    * object.
290    */

291   QueryPlanNode createViewQueryPlanNode(TableName table_name) {
292     return view_manager.createViewQueryPlanNode(table_name);
293   }
294   
295   /**
296    * Returns a java.sql.Connection object that can be used as a JDBC
297    * interface to access the current transaction of this DatabaseConnection.
298    * <p>
299    * There are a few important considerations when using the JDBC connection;
300    * <ul>
301    * <li>The returned Connection does not allow auto-commit to be set. It
302    * is intended to be used to issue commands to this
303    * DatabaseConnection from inside a transaction so auto-commit does
304    * not make sense.
305    * <li>The returned object must only be accessed from the same worker
306    * thread that is currently accessing this DatabaseConnection. The
307    * returned Connection is <b>NOT</b> multi-thread capable.
308    * <li>The java.sql.Connection returned here is invalidated (disposed) when
309    * the current transaction is closed (committed or rolled back).
310    * <li>This method returns the same java.sql.Connection on multiple calls
311    * to this method (while a transaction is open).
312    * <li>The DatabaseConnection must be locked in EXCLUSIVE mode or the
313    * queries will fail.
314    * </ul>
315    */

316   public java.sql.Connection JavaDoc getJDBCConnection() {
317     if (jdbc_connection == null) {
318       jdbc_connection =
319                      InternalJDBCHelper.createJDBCConnection(getUser(), this);
320     }
321     return jdbc_connection;
322   }
323
324   /**
325    * Creates an object that implements ProcedureConnection that provides access
326    * to this connection.
327    * <p>
328    * Note that this connection is set to the user of the privs that the
329    * procedure executes under when this method returns.
330    * <p>
331    * There must be a 100% guarentee that after this method is called, a call to
332    * 'disposeProcedureConnection' is called which cleans up the state of this
333    * object.
334    */

335   ProcedureConnection createProcedureConnection(User user) {
336     // Create the ProcedureConnection object,
337
DCProcedureConnection c = new DCProcedureConnection();
338     // Record the current user
339
c.previous_user = getUser();
340     // Record the current 'close_transaction_disabled' flag
341
c.transaction_disabled_flag = close_transaction_disabled;
342     // Set the new user
343
setUser(user);
344     // Disable the ability to close a transaction
345
close_transaction_disabled = true;
346     // Return
347
return c;
348   }
349
350   /**
351    * Disposes of the ProcedureConnection that was previously created by the
352    * 'createProcedure' method.
353    */

354   void disposeProcedureConnection(ProcedureConnection connection) {
355     DCProcedureConnection c = (DCProcedureConnection) connection;
356     // Revert back to the previous user.
357
setUser(c.previous_user);
358     // Revert back to the previous transaction disable status.
359
close_transaction_disabled = c.transaction_disabled_flag;
360     // Dispose of the connection
361
c.dispose();
362   }
363
364   /**
365    * Returns the DatabaseSystem object for this connection.
366    */

367   public DatabaseSystem getSystem() {
368     return database.getSystem();
369   }
370
371   /**
372    * Returns the Database object for this connection.
373    */

374   public Database getDatabase() {
375     return database;
376   }
377
378   /**
379    * Returns the conglomerate of this connection.
380    */

381   TableDataConglomerate getConglomerate() {
382     return conglomerate;
383   }
384   
385   /**
386    * Sets the User object for this connection. This is necessary because we
387    * may want to temporarily change the user on this connection to allow
388    * top level queries in a different privilege space.
389    */

390   void setUser(User user) {
391     this.user = user;
392   }
393   
394   /**
395    * Returns the User object for this connection.
396    */

397   public User getUser() {
398     return user;
399   }
400
401   /**
402    * Returns a DebugLogger object that we can use to log debug messages to.
403    */

404   public final DebugLogger Debug() {
405     return logger;
406   }
407
408   /**
409    * Returns the connection trigger manager for this connection.
410    */

411   public ConnectionTriggerManager getConnectionTriggerManager() {
412     return connection_trigger_manager;
413   }
414   
415   /**
416    * Returns the GrantManager object that manages grants for tables in the
417    * database for this connection/user.
418    */

419   public GrantManager getGrantManager() {
420     return grant_manager;
421   }
422
423   /**
424    * Returns the ProcedureManager object that manages database functions and
425    * procedures in the database for this connection/user.
426    */

427   public ProcedureManager getProcedureManager() {
428     return procedure_manager;
429   }
430   
431   /**
432    * Sets the auto-commit mode.
433    */

434   public void setAutoCommit(boolean status) {
435     auto_commit = status;
436   }
437
438   /**
439    * Sets the transaction isolation level from a string.
440    */

441   public void setTransactionIsolation(String JavaDoc name) {
442     if (name.equals("serializable")) {
443       transaction_isolation = 4;
444     }
445     else {
446       throw new Error JavaDoc("Can not set transaction isolation to " + name);
447     }
448   }
449
450   /**
451    * Assigns a variable to the expression for this connection. This is a
452    * generic way of setting properties of the connection. Currently supported
453    * variables are;
454    * <p>
455    * ERROR_ON_DIRTY_SELECT - set to Boolean.TRUE for turning this transaction
456    * conflict off on this connection.
457    * CASE_INSENSITIVE_IDENTIFIERS - Boolean.TRUE means the grammar becomes
458    * case insensitive for identifiers resolved by the grammar.
459    */

460   public void setVar(String JavaDoc name, Expression exp) {
461     if (name.toUpperCase().equals("ERROR_ON_DIRTY_SELECT")) {
462       error_on_dirty_select = toBooleanValue(exp);
463     }
464     else if (name.toUpperCase().equals("CASE_INSENSITIVE_IDENTIFIERS")) {
465       case_insensitive_identifiers = toBooleanValue(exp);
466     }
467   }
468
469   /**
470    * Evaluates the expression to a boolean value (true or false).
471    */

472   private static boolean toBooleanValue(Expression exp) {
473     Boolean JavaDoc b = exp.evaluate(null, null, null).toBoolean();
474     if (b == null) {
475       throw new StatementException(
476                  "Expression does not evaluate to a boolean (true or false).");
477     }
478     return b.booleanValue();
479   }
480
481   /**
482    * Returns the auto-commit status of this connection. If this is true then
483    * the language layer must execute a COMMIT after every statement.
484    */

485   public boolean getAutoCommit() {
486     return auto_commit;
487   }
488
489   /**
490    * Returns the transaction isolation level of this connection.
491    */

492   public int getTransactionIsolation() {
493     return transaction_isolation;
494   }
495
496   /**
497    * Returns the transaction isolation level of this connection as a string.
498    */

499   public String JavaDoc getTransactionIsolationAsString() {
500     int il = getTransactionIsolation();
501     if (il == 1) {
502       return "read uncommitted";
503     }
504     else if (il == 2) {
505       return "read committed";
506     }
507     else if (il == 3) {
508       return "repeatable read";
509     }
510     else if (il == 4) {
511       return "serializable";
512     }
513     else {
514       return "unknown isolation level";
515     }
516   }
517
518   /**
519    * Returns the name of the schema that this connection is within.
520    */

521   public String JavaDoc getCurrentSchema() {
522     return current_schema;
523   }
524
525   /**
526    * Returns true if the connection is in case insensitive mode. In case
527    * insensitive mode the case of identifier strings is not important.
528    */

529   public boolean isInCaseInsensitiveMode() {
530     return case_insensitive_identifiers;
531   }
532
533   /**
534    * Sets the schema that this connection is within.
535    */

536   public void setCurrentSchema(String JavaDoc current_schema) {
537     this.current_schema = current_schema;
538   }
539
540   /**
541    * Returns the LockingMechanism object that is within the context of this
542    * database connection. This manages read/write locking within this
543    * connection.
544    */

545   public LockingMechanism getLockingMechanism() {
546     return locking_mechanism;
547   }
548
549   /**
550    * Attaches a TableBackedCache object to this DatabaseConnection which is
551    * notified when a transaction is started and stopped, and when the table
552    * being backed has changes made to it.
553    */

554   void attachTableBackedCache(TableBackedCache cache) {
555     cache.attachTo(conglomerate);
556     table_backed_cache_list.add(cache);
557   }
558   
559   /**
560    * Returns a TableName[] array that contains the list of database
561    * tables that are visible by this transaction.
562    * <p>
563    * This returns the list of all objects that represent queriable tables in
564    * the database.
565    */

566   public TableName[] getTableList() {
567     return getTransaction().getTableList();
568   }
569
570   /**
571    * Returns true if the table exists within this connection transaction.
572    */

573   public boolean tableExists(String JavaDoc table_name) {
574     return tableExists(new TableName(current_schema, table_name));
575   }
576
577   /**
578    * Returns true if the table exists within this connection transaction.
579    */

580   public boolean tableExists(TableName table_name) {
581     table_name = substituteReservedTableName(table_name);
582     return getTransaction().tableExists(table_name);
583   }
584
585   /**
586    * Returns the type of the table object. Currently this is either "TABLE"
587    * or "VIEW".
588    */

589   public String JavaDoc getTableType(TableName table_name) {
590     table_name = substituteReservedTableName(table_name);
591     return getTransaction().getTableType(table_name);
592   }
593
594   /**
595    * Attempts to resolve the given table name to its correct case assuming
596    * the table name represents a case insensitive version of the name. For
597    * example, "aPP.CuSTOMer" may resolve to "APP.Customer". If the table
598    * name can not resolve to a valid identifier it returns the input table
599    * name, therefore the actual presence of the table should always be
600    * checked by calling 'tableExists' after this method returns.
601    */

602   public TableName tryResolveCase(TableName table_name) {
603     table_name = substituteReservedTableName(table_name);
604     table_name = getTransaction().tryResolveCase(table_name);
605     return table_name;
606   }
607
608   /**
609    * Resolves a TableName string (eg. 'Customer' 'APP.Customer' ) to a
610    * TableName object. If the schema part of the table name is not present
611    * then it is set to the current schema of the database connection. If the
612    * database is ignoring the case then this will correctly resolve the table
613    * to the cased version of the table name.
614    */

615   public TableName resolveTableName(String JavaDoc name) {
616     TableName table_name = TableName.resolve(getCurrentSchema(), name);
617     table_name = substituteReservedTableName(table_name);
618     if (isInCaseInsensitiveMode()) {
619       // Try and resolve the case of the table name,
620
table_name = tryResolveCase(table_name);
621     }
622     return table_name;
623   }
624
625   /**
626    * Resolves the given string to a table name, throwing an exception if
627    * the reference is ambiguous. This also generates an exception if the
628    * table object is not found.
629    */

630   public TableName resolveToTableName(String JavaDoc name) {
631     TableName table_name = TableName.resolve(getCurrentSchema(), name);
632     if (table_name.getName().equalsIgnoreCase("OLD")) {
633       return Database.OLD_TRIGGER_TABLE;
634     }
635     else if (table_name.getName().equalsIgnoreCase("NEW")) {
636       return Database.NEW_TRIGGER_TABLE;
637     }
638
639     return getTransaction().resolveToTableName(getCurrentSchema(), name,
640                                                isInCaseInsensitiveMode());
641
642   }
643
644   /**
645    * Returns the DataTableDef for the table with the given name.
646    */

647   public DataTableDef getDataTableDef(TableName name) {
648     name = substituteReservedTableName(name);
649     return getTransaction().getDataTableDef(name);
650   }
651
652   /**
653    * Returns a DataTable that represents the table from the given schema,
654    * name in the database.
655    */

656   public DataTable getTable(TableName name) {
657     name = substituteReservedTableName(name);
658
659     try {
660       // Special handling of NEW and OLD table, we cache the DataTable in the
661
// OldNewTableState object,
662
if (name.equals(Database.OLD_TRIGGER_TABLE)) {
663         if (current_old_new_state.OLD_data_table == null) {
664           current_old_new_state.OLD_data_table =
665                           new DataTable(this, getTransaction().getTable(name));
666         }
667         return current_old_new_state.OLD_data_table;
668       }
669       else if (name.equals(Database.NEW_TRIGGER_TABLE)) {
670         if (current_old_new_state.NEW_data_table == null) {
671           current_old_new_state.NEW_data_table =
672                           new DataTable(this, getTransaction().getTable(name));
673         }
674         return current_old_new_state.NEW_data_table;
675       }
676
677       // Ask the transaction for the table
678
MutableTableDataSource table = getTransaction().getTable(name);
679
680       // Is this table in the tables_cache?
681
DataTable dtable = (DataTable) tables_cache.get(table);
682       // No, so wrap it around a Datatable and put it in the cache
683
if (dtable == null) {
684         dtable = new DataTable(this, table);
685         tables_cache.put(table, dtable);
686       }
687       // Return the DataTable
688
return dtable;
689
690     }
691     catch (DatabaseException e) {
692       Debug().writeException(e);
693       throw new Error JavaDoc("Database Exception: " + e.getMessage());
694     }
695
696   }
697
698   /**
699    * Returns a DataTable that represents the table with the given name in the
700    * database from the current connection schema.
701    */

702   public DataTable getTable(String JavaDoc table_name) {
703     return getTable(new TableName(current_schema, table_name));
704   }
705
706   /**
707    * Create a new table within the context of the current connection
708    * transaction.
709    */

710   public void createTable(DataTableDef table_def) {
711     checkAllowCreate(table_def.getTableName());
712     getTransaction().createTable(table_def);
713   }
714
715   /**
716    * Create a new table with a starting initial sector size. This should
717    * only be used as very fine grain optimization for creating tables. If
718    * in the future the underlying table model is changed so that the given
719    * 'sector_size' value is unapplicable, then the value will be ignored.
720    */

721   public void createTable(DataTableDef table_def,
722                           int data_sector_size, int index_sector_size) {
723     checkAllowCreate(table_def.getTableName());
724     getTransaction().createTable(table_def,
725                                  data_sector_size, index_sector_size);
726   }
727
728   /**
729    * Creates a new view. This takes the information in the ViewDef object and
730    * adds it to the system view table.
731    * <p>
732    * Note that this is a transactional operation. You need to commit for the
733    * view to be visible to other transactions.
734    */

735   public void createView(SQLQuery query, ViewDef view) {
736     checkAllowCreate(view.getDataTableDef().getTableName());
737
738     try {
739       view_manager.defineView(view, query, getUser());
740     }
741     catch (DatabaseException e) {
742       Debug().writeException(e);
743       throw new RuntimeException JavaDoc("Database Exception: " + e.getMessage());
744     }
745
746   }
747
748   /**
749    * Drops the view with the given name and returns true if the drop succeeded.
750    * Returns false if the view was not found.
751    * <p>
752    * Note that this is a transactional operation. You need to commit for the
753    * change to be visible to other transactions.
754    */

755   public boolean dropView(TableName view_name) {
756
757     try {
758       return view_manager.deleteView(view_name);
759     }
760     catch (DatabaseException e) {
761       Debug().writeException(e);
762       throw new RuntimeException JavaDoc("Database Exception: " + e.getMessage());
763     }
764
765   }
766   
767   /**
768    * Updates a given table within the context of the current connection
769    * transaction.
770    */

771   public void updateTable(DataTableDef table_def) {
772     checkAllowCreate(table_def.getTableName());
773     getTransaction().alterTable(table_def.getTableName(), table_def);
774   }
775
776   /**
777    * Updates a given table within the context of the current connection
778    * transaction. This should only be used as very fine grain optimization
779    * for creating tables.If in the future the underlying table model is
780    * changed so that the given 'sector_size' value is unapplicable, then the
781    * value will be ignored.
782    */

783   public void updateTable(DataTableDef table_def,
784                           int data_sector_size, int index_sector_size) {
785     checkAllowCreate(table_def.getTableName());
786     getTransaction().alterTable(table_def.getTableName(), table_def,
787                                 data_sector_size, index_sector_size);
788   }
789
790   /**
791    * Given a DataTableDef, if the table exists then it is updated otherwise
792    * if it doesn't exist then it is created.
793    * <p>
794    * This should only be used as very fine grain optimization for creating/
795    * altering tables. If in the future the underlying table model is changed
796    * so that the given 'sector_size' value is unapplicable, then the value
797    * will be ignored.
798    */

799   public void alterCreateTable(DataTableDef table_def,
800                                int data_sector_size, int index_sector_size) {
801     if (!tableExists(table_def.getTableName())) {
802       createTable(table_def, data_sector_size, index_sector_size);
803     }
804     else {
805       updateTable(table_def, data_sector_size, index_sector_size);
806     }
807   }
808
809   /**
810    * Given a DataTableDef, if the table exists then it is updated otherwise
811    * if it doesn't exist then it is created.
812    */

813   public void alterCreateTable(DataTableDef table_def) {
814     if (!tableExists(table_def.getTableName())) {
815       createTable(table_def);
816     }
817     else {
818       updateTable(table_def);
819     }
820   }
821
822   /**
823    * Notifies this transaction that a database object with the given name has
824    * successfully been created.
825    */

826   void databaseObjectCreated(TableName table_name) {
827     getTransaction().databaseObjectCreated(table_name);
828   }
829
830   /**
831    * Notifies this transaction that a database object with the given name has
832    * successfully been dropped.
833    */

834   void databaseObjectDropped(TableName table_name) {
835     getTransaction().databaseObjectDropped(table_name);
836   }
837
838   /**
839    * Checks all the rows in the table for immediate constraint violations
840    * and when the transaction is next committed check for all deferred
841    * constraint violations. This method is used when the constraints on a
842    * table changes and we need to determine if any constraint violations
843    * occurred. To the constraint checking system, this is like adding all
844    * the rows to the given table.
845    */

846   public void checkAllConstraints(TableName table_name) {
847     // Assert
848
checkExclusive();
849     getTransaction().checkAllConstraints(table_name);
850   }
851
852   /**
853    * Drops a table from within the context of the current connection
854    * transaction.
855    */

856   public void dropTable(String JavaDoc table_name) {
857     dropTable(new TableName(current_schema, table_name));
858   }
859
860   /**
861    * Drops a table from within the context of the current connection
862    * transaction.
863    */

864   public void dropTable(TableName table_name) {
865     getTransaction().dropTable(table_name);
866   }
867
868   /**
869    * Compacts the table with the given name. Throws an exception if the
870    * table doesn't exist.
871    */

872   public void compactTable(String JavaDoc table_name) {
873     compactTable(new TableName(current_schema, table_name));
874   }
875
876   /**
877    * Compacts the table with the given name. Throws an exception if the
878    * table doesn't exist.
879    */

880   public void compactTable(TableName table_name) {
881     getTransaction().compactTable(table_name);
882   }
883
884   /**
885    * Adds the given table name to the list of tables that are selected from
886    * within the transaction in this connection.
887    */

888   public void addSelectedFromTable(String JavaDoc table_name) {
889     addSelectedFromTable(new TableName(current_schema, table_name));
890   }
891
892   /**
893    * Adds the given table name to the list of tables that are selected from
894    * within the transaction in this connection.
895    */

896   public void addSelectedFromTable(TableName name) {
897     getTransaction().addSelectedFromTable(name);
898   }
899
900   /**
901    * Requests of the sequence generator the next value from the sequence.
902    * <p>
903    * NOTE: This does NOT check that the user owning this connection has the
904    * correct privs to perform this operation.
905    */

906   public long nextSequenceValue(String JavaDoc name) {
907     // Resolve and ambiguity test
908
TableName seq_name = resolveToTableName(name);
909     return getTransaction().nextSequenceValue(seq_name);
910   }
911
912   /**
913    * Returns the current sequence value for the given sequence generator that
914    * was last returned by a call to 'nextSequenceValue'. If a value was not
915    * last returned by a call to 'nextSequenceValue' then a statement exception
916    * is generated.
917    * <p>
918    * NOTE: This does NOT check that the user owning this connection has the
919    * correct privs to perform this operation.
920    */

921   public long lastSequenceValue(String JavaDoc name) {
922     // Resolve and ambiguity test
923
TableName seq_name = resolveToTableName(name);
924     return getTransaction().lastSequenceValue(seq_name);
925   }
926
927   /**
928    * Sets the sequence value for the given sequence generator. If the generator
929    * does not exist or it is not possible to set the value for the generator
930    * then an exception is generated.
931    * <p>
932    * NOTE: This does NOT check that the user owning this connection has the
933    * correct privs to perform this operation.
934    */

935   public void setSequenceValue(String JavaDoc name, long value) {
936     // Resolve and ambiguity test
937
TableName seq_name = resolveToTableName(name);
938     getTransaction().setSequenceValue(seq_name, value);
939   }
940   
941   /**
942    * Returns the next unique identifier for the given table from the schema.
943    */

944   public long nextUniqueID(TableName name) {
945     return getTransaction().nextUniqueID(name);
946   }
947
948   /**
949    * Returns the next unique identifier for the given table in the connection
950    * schema.
951    */

952   public long nextUniqueID(String JavaDoc table_name) {
953     TableName tname = TableName.resolve(current_schema, table_name);
954     return nextUniqueID(tname);
955   }
956
957   /**
958    * If the given table name is a reserved name, then we must substitute it
959    * with its correct form. For example, 'APP.NEW' becomes 'SYS_INFO.NEW',
960    * etc.
961    */

962   static TableName substituteReservedTableName(TableName table_name) {
963     // We do not allow tables to be created with a reserved name
964
String JavaDoc name = table_name.getName();
965     if (name.equalsIgnoreCase("OLD")) {
966       return Database.OLD_TRIGGER_TABLE;
967     }
968     if (name.equalsIgnoreCase("NEW")) {
969       return Database.NEW_TRIGGER_TABLE;
970     }
971     return table_name;
972   }
973   
974   /**
975    * Generates an exception if the name of the table is reserved and the
976    * creation of the table should be prevented. For example, the table
977    * names 'OLD' and 'NEW' are reserved.
978    */

979   static void checkAllowCreate(TableName table_name) {
980     // We do not allow tables to be created with a reserved name
981
String JavaDoc name = table_name.getName();
982     if (name.equalsIgnoreCase("OLD") ||
983         name.equalsIgnoreCase("NEW")) {
984       throw new StatementException("Table name '" + table_name +
985                                    "' is reserved.");
986     }
987   }
988   
989   /**
990    * Creates a new sequence generator with the given TableName and
991    * initializes it with the given details. This does NOT check if the
992    * given name clashes with an existing database object.
993    */

994   public void createSequenceGenerator(
995                TableName name, long start_value, long increment_by,
996                long min_value, long max_value, long cache, boolean cycle) {
997
998     // Check the name of the database object isn't reserved (OLD/NEW)
999
checkAllowCreate(name);
1000
1001    getTransaction().createSequenceGenerator(name,
1002               start_value, increment_by, min_value, max_value, cache, cycle);
1003  }
1004
1005  /**
1006   * Drops an existing sequence generator with the given name.
1007   */

1008  public void dropSequenceGenerator(TableName name) {
1009    getTransaction().dropSequenceGenerator(name);
1010  }
1011  
1012  /**
1013   * Adds a type of trigger for the given trigger source (usually the
1014   * name of the table).
1015   * <p>
1016   * Adds a type of trigger to the given Table. When the event is fired, the
1017   * UserCallBack method is notified of the event.
1018   */

1019  public void createTrigger(String JavaDoc trigger_name,
1020                            String JavaDoc trigger_source, int type) {
1021    database.getTriggerManager().addTriggerListener(
1022                              this, trigger_name, type, trigger_source, this);
1023  }
1024
1025  /**
1026   * Removes a type of trigger for the given trigger source (usually the
1027   * name of the table).
1028   */

1029  public void deleteTrigger(String JavaDoc trigger_name) {
1030    database.getTriggerManager().removeTriggerListener(this, trigger_name);
1031  }
1032
1033  /**
1034   * Informs the underlying transaction that a high level transaction event
1035   * has occurred and should be dispatched to any listeners occordingly.
1036   */

1037  public void notifyTriggerEvent(TriggerEvent evt) {
1038    trigger_event_list.add(evt);
1039  }
1040
1041  /**
1042   * Allocates a new large object in the Blob store of this conglomerate of the
1043   * given type and size. The blob data must be written through the
1044   * Ref after the large object is created. Once the data has been written
1045   * the 'complete' method in Ref is called.
1046   * <p>
1047   * Once a large object is created and written to, it may be allocated in one
1048   * or more tables in the conglomerate.
1049   */

1050  public Ref createNewLargeObject(byte type, long object_size) {
1051    // Enable compression for string types (but not binary types).
1052
if (type == 3 || type == 4) {
1053      type = (byte) (type | 0x010);
1054    }
1055    return conglomerate.createNewLargeObject(type, object_size);
1056  }
1057
1058  /**
1059   * Tells the conglomerate to flush the blob store. This should be called
1060   * after one or more blobs have been created and the data for the blob(s) are
1061   * set. It is an important step to perform AFTER blobs have been written.
1062   * <p>
1063   * If this is not called and the database closes (or crashes) before a flush
1064   * occurs then the blob may not be recoverable.
1065   */

1066  public void flushBlobStore() {
1067    conglomerate.flushBlobStore();
1068  }
1069
1070  /**
1071   * Returns a TableQueryDef object that describes the characteristics of a
1072   * table including the name (TableName), the columns (DataTableDef) and the
1073   * query plan to produce the table (QueryPlanNode). This object can be used
1074   * to resolve information about a particular table, and to evaluate the
1075   * query plan to produce the table itself.
1076   * <p>
1077   * This produces TableQueryDef objects for all table objects in the database
1078   * including data tables and views.
1079   * <p>
1080   * The 'aliased_as' parameter is used to overwrite the default name of the
1081   * table object.
1082   */

1083  public TableQueryDef getTableQueryDef(final TableName table_name,
1084                                        final TableName aliased_as) {
1085
1086    // Produce the data table def for this database object.
1087
DataTableDef dtf = getDataTableDef(table_name);
1088    // If the table is aliased, set a new DataTableDef with the given name
1089
if (aliased_as != null) {
1090      dtf = new DataTableDef(dtf);
1091      dtf.setTableName(aliased_as);
1092      dtf.setImmutable();
1093    }
1094    final DataTableDef data_table_def = dtf;
1095// final String aliased_name =
1096
// aliased_as == null ? null : aliased_as.getName();
1097

1098    return new TableQueryDef() {
1099      public DataTableDef getDataTableDef() {
1100        return data_table_def;
1101      }
1102      public QueryPlanNode getQueryPlanNode() {
1103        return createObjectFetchQueryPlan(table_name, aliased_as);
1104      }
1105    };
1106                                          
1107  }
1108
1109  /**
1110   * Creates a QueryPlanNode to fetch the given table object from this
1111   * connection.
1112   */

1113  public QueryPlanNode createObjectFetchQueryPlan(TableName table_name,
1114                                                  TableName aliased_name) {
1115    String JavaDoc table_type = getTableType(table_name);
1116    if (table_type.equals("VIEW")) {
1117      return new QueryPlan.FetchViewNode(table_name, aliased_name);
1118    }
1119    else {
1120      return new QueryPlan.FetchTableNode(table_name, aliased_name);
1121    }
1122  }
1123  
1124  // ---------- Schema management and constraint methods ----------
1125
// Methods that handle getting/setting schema information such as;
1126
// * Creating/dropping/querying schema
1127
// * Creating/dropping/querying constraint information including;
1128
// check constraints, unique constraints, primary key constraints,
1129
// foreign key constraints, etc.
1130

1131  /**
1132   * Changes the default schema to the given schema.
1133   */

1134  public void setDefaultSchema(String JavaDoc schema_name) {
1135    boolean ignore_case = isInCaseInsensitiveMode();
1136    SchemaDef schema = resolveSchemaCase(schema_name, ignore_case);
1137    if (schema == null) {
1138      throw new Error JavaDoc("Schema '" + schema_name + "' does not exist.");
1139    }
1140    else {
1141      // Set the default schema for this connection
1142
setCurrentSchema(schema.getName());
1143    }
1144  }
1145
1146  // NOTE: These methods are copied because they simply call through to the
1147
// Transaction implementation of the method with the same signature.
1148

1149  private void checkExclusive() {
1150    if (!getLockingMechanism().isInExclusiveMode()) {
1151      throw new Error JavaDoc("Assertion failed: Expected to be in exclusive mode.");
1152    }
1153  }
1154
1155  /**
1156   * Same as the Transaction.createSchema method.
1157   */

1158  public void createSchema(String JavaDoc name, String JavaDoc type) {
1159    // Assert
1160
checkExclusive();
1161    getTransaction().createSchema(name, type);
1162  }
1163
1164  /**
1165   * Same as the Transaction.dropSchema method.
1166   */

1167  public void dropSchema(String JavaDoc name) {
1168    // Assert
1169
checkExclusive();
1170    getTransaction().dropSchema(name);
1171  }
1172
1173  /**
1174   * Same as the Transaction.schemaExists method.
1175   */

1176  public boolean schemaExists(String JavaDoc name) {
1177    return getTransaction().schemaExists(name);
1178  }
1179
1180  /**
1181   * Same as the Transaction.resolveSchemaCase method.
1182   */

1183  public SchemaDef resolveSchemaCase(String JavaDoc name, boolean ignore_case) {
1184    return getTransaction().resolveSchemaCase(name, ignore_case);
1185  }
1186
1187  /**
1188   * Convenience - returns the SchemaDef object given the name of the schema.
1189   * If identifiers are case insensitive, we resolve the case of the schema
1190   * name also.
1191   */

1192  public SchemaDef resolveSchemaName(String JavaDoc name) {
1193    boolean ignore_case = isInCaseInsensitiveMode();
1194    return resolveSchemaCase(name, ignore_case);
1195  }
1196  
1197  /**
1198   * Same as the Transaction.getSchemaList method.
1199   */

1200  public SchemaDef[] getSchemaList() {
1201    return getTransaction().getSchemaList();
1202  }
1203  
1204  /**
1205   * Same as the Transaction.setPersistentVar method.
1206   */

1207  public void setPersistentVar(String JavaDoc variable, String JavaDoc value) {
1208    // Assert
1209
checkExclusive();
1210    getTransaction().setPersistentVar(variable, value);
1211  }
1212
1213  /**
1214   * Same as the Transaction.getPersistentVar method.
1215   */

1216  public String JavaDoc getPersistentVar(String JavaDoc variable) {
1217    return getTransaction().getPersistantVar(variable);
1218  }
1219
1220  /**
1221   * Same as the Transaction.addUniqueConstraint method.
1222   */

1223  public void addUniqueConstraint(TableName table_name, String JavaDoc[] cols,
1224                                  short deferred, String JavaDoc constraint_name) {
1225    // Assert
1226
checkExclusive();
1227    getTransaction().addUniqueConstraint(table_name, cols,
1228                                         deferred, constraint_name);
1229  }
1230
1231  /**
1232   * Same as the Transaction.addForeignKeyConstraint method.
1233   */

1234  public void addForeignKeyConstraint(TableName table, String JavaDoc[] cols,
1235                                      TableName ref_table, String JavaDoc[] ref_cols,
1236                                      String JavaDoc delete_rule, String JavaDoc update_rule,
1237                                      short deferred, String JavaDoc constraint_name) {
1238    // Assert
1239
checkExclusive();
1240    getTransaction().addForeignKeyConstraint(table, cols, ref_table, ref_cols,
1241                                             delete_rule, update_rule,
1242                                             deferred, constraint_name);
1243  }
1244
1245  /**
1246   * Same as the Transaction.addPrimaryKeyConstraint method.
1247   */

1248  public void addPrimaryKeyConstraint(TableName table_name, String JavaDoc[] cols,
1249                                      short deferred, String JavaDoc constraint_name) {
1250    // Assert
1251
checkExclusive();
1252    getTransaction().addPrimaryKeyConstraint(table_name, cols,
1253                                             deferred, constraint_name);
1254  }
1255
1256  /**
1257   * Same as the Transaction.addCheckConstraint method.
1258   */

1259  public void addCheckConstraint(TableName table_name,
1260               Expression expression, short deferred, String JavaDoc constraint_name) {
1261    // Assert
1262
checkExclusive();
1263    getTransaction().addCheckConstraint(table_name, expression,
1264                                        deferred, constraint_name);
1265  }
1266
1267  /**
1268   * Same as the Transaction.dropAllConstraintsForTable method.
1269   */

1270  public void dropAllConstraintsForTable(TableName table_name) {
1271    // Assert
1272
checkExclusive();
1273    getTransaction().dropAllConstraintsForTable(table_name);
1274  }
1275
1276  /**
1277   * Same as the Transaction.dropNamedConstraint method.
1278   */

1279  public int dropNamedConstraint(TableName table_name,
1280                                 String JavaDoc constraint_name) {
1281    // Assert
1282
checkExclusive();
1283    return getTransaction().dropNamedConstraint(table_name, constraint_name);
1284  }
1285
1286  /**
1287   * Same as the Transaction.dropPrimaryKeyConstraintForTable method.
1288   */

1289  public boolean dropPrimaryKeyConstraintForTable(
1290                              TableName table_name, String JavaDoc constraint_name) {
1291    // Assert
1292
checkExclusive();
1293    return getTransaction().dropPrimaryKeyConstraintForTable(table_name,
1294                                                             constraint_name);
1295  }
1296
1297  /**
1298   * Same as the Transaction.queryTablesRelationallyLinkedTo method.
1299   */

1300  public TableName[] queryTablesRelationallyLinkedTo(TableName table) {
1301    return Transaction.queryTablesRelationallyLinkedTo(getTransaction(), table);
1302  }
1303
1304  /**
1305   * Same as the Transaction.queryTableUniqueGroups method.
1306   */

1307  public Transaction.ColumnGroup[] queryTableUniqueGroups(
1308                                               TableName table_name) {
1309    return Transaction.queryTableUniqueGroups(getTransaction(), table_name);
1310  }
1311
1312  /**
1313   * Same as the Transaction.queryTablePrimaryKeyGroup method.
1314   */

1315  public Transaction.ColumnGroup queryTablePrimaryKeyGroup(
1316                                               TableName table_name) {
1317    return Transaction.queryTablePrimaryKeyGroup(getTransaction(), table_name);
1318  }
1319
1320  /**
1321   * Same as the Transaction.queryTableCheckExpression method.
1322   */

1323  public Transaction.CheckExpression[] queryTableCheckExpressions(
1324                                               TableName table_name) {
1325    return Transaction.queryTableCheckExpressions(getTransaction(), table_name);
1326  }
1327
1328  /**
1329   * Same as the Transaction.queryTableForeignKeyReferences method.
1330   */

1331  public Transaction.ColumnGroupReference[] queryTableForeignKeyReferences(
1332                                                      TableName table_name) {
1333    return Transaction.queryTableForeignKeyReferences(getTransaction(),
1334                                                      table_name);
1335  }
1336
1337  /**
1338   * Same as the Transaction.queryTableImportedForeignKeyReferences method.
1339   */

1340  public Transaction.ColumnGroupReference[]
1341               queryTableImportedForeignKeyReferences(TableName table_name) {
1342    return Transaction.queryTableImportedForeignKeyReferences(getTransaction(),
1343                                                              table_name);
1344  }
1345
1346
1347  // ---------- Triggered OLD/NEW table handling ----------
1348
// These methods are used by the ConnectionTriggerManager object to
1349
// temporarily create OLD and NEW tables in this connection from inside a
1350
// triggered action. In some cases (before the operation) the OLD table
1351
// is mutable.
1352

1353  /**
1354   * Returns the current state of the old/new tables.
1355   */

1356  OldNewTableState getOldNewTableState() {
1357    return current_old_new_state;
1358  }
1359
1360  /**
1361   * Sets the current state of the old/new tables. When nesting OLD/NEW
1362   * tables for nested stored procedures, the current state should be first
1363   * recorded and reverted back when the nested procedure finishes.
1364   */

1365  void setOldNewTableState(OldNewTableState state) {
1366    current_old_new_state = state;
1367  }
1368  
1369  // ---------- Trigger methods ----------
1370

1371  /**
1372   * Notifies this connection that an insert/delete or update operation has
1373   * occurred on some table of this DatabaseConnection. This should notify
1374   * the trigger connection manager of this event so that it may perform any
1375   * action that may have been set up to occur on this event.
1376   */

1377  void fireTableEvent(TableModificationEvent evt) {
1378    connection_trigger_manager.performTriggerAction(evt);
1379  }
1380
1381  // ---------- Implemented from TriggerListener ----------
1382

1383  /**
1384   * Notifies when a trigger has fired for this user. If there are no open
1385   * transactions on this connection then we do a straight call back trigger
1386   * notify. If there is a transaction open then trigger events are added
1387   * to the 'trigger_event_buffer' which fires when the connection transaction
1388   * is committed or rolled back.
1389   */

1390  public void fireTrigger(DatabaseConnection database, String JavaDoc trigger_name,
1391                          TriggerEvent evt) {
1392
1393    if (this != database) {
1394      throw new Error JavaDoc("User object mismatch.");
1395    }
1396
1397    try {
1398      // Did we pass in a call back interface?
1399
if (call_back != null) {
1400        synchronized (trigger_event_buffer) {
1401          // If there is no active transaction then fire trigger immediately.
1402
if (transaction == null) {
1403            call_back.triggerNotify(trigger_name, evt.getType(),
1404                                    evt.getSource(), evt.getCount());
1405          }
1406          // Otherwise add to buffer
1407
else {
1408            trigger_event_buffer.add(trigger_name);
1409            trigger_event_buffer.add(evt);
1410          }
1411        }
1412      }
1413    }
1414    catch (Throwable JavaDoc e) {
1415      Debug().write(Lvl.ERROR, this, "TRIGGER Exception: " + e.getMessage());
1416    }
1417  }
1418
1419  /**
1420   * Fires any triggers that are pending in the trigger buffer.
1421   */

1422  private void firePendingTriggerEvents() {
1423    int sz;
1424    synchronized (trigger_event_buffer) {
1425      sz = trigger_event_buffer.size();
1426    }
1427    if (sz > 0) {
1428      // Post an event that fires the triggers for each listener.
1429
Runnable JavaDoc runner = new Runnable JavaDoc() {
1430        public void run() {
1431          synchronized (trigger_event_buffer) {
1432            // Fire all pending trigger events in buffer
1433
for (int i = 0; i < trigger_event_buffer.size(); i += 2) {
1434              String JavaDoc trigger_name = (String JavaDoc) trigger_event_buffer.get(i);
1435              TriggerEvent evt =
1436                          (TriggerEvent) trigger_event_buffer.get(i + 1);
1437              call_back.triggerNotify(trigger_name, evt.getType(),
1438                                      evt.getSource(), evt.getCount());
1439            }
1440            // Clear the buffer
1441
trigger_event_buffer.clear();
1442          }
1443        }
1444      };
1445
1446      // Post the event to go off approx 3ms from now.
1447
database.postEvent(3, database.createEvent(runner));
1448    }
1449
1450  }
1451
1452  /**
1453   * Private method that disposes the current transaction.
1454   */

1455  private void disposeTransaction() {
1456    // Set the transaction to null
1457
transaction = null;
1458    // Fire any pending trigger events in the trigger buffer.
1459
firePendingTriggerEvents();
1460    // Clear the trigger events in this object
1461
trigger_event_list.clear();
1462    
1463    // Notify any table backed caches that this transaction has finished.
1464
int sz = table_backed_cache_list.size();
1465      for (int i = 0; i < sz; ++i) {
1466      TableBackedCache cache =
1467                           (TableBackedCache) table_backed_cache_list.get(i);
1468      cache.transactionFinished();
1469    }
1470  }
1471
1472  /**
1473   * Tries to commit the current transaction. If the transaction can not be
1474   * committed because there were concurrent changes that interfered with
1475   * each other then a TransactionError is thrown and the transaction is
1476   * rolled back.
1477   * <p>
1478   * NOTE: It's guarenteed that the transaction will be closed even if a
1479   * transaction exception occurs.
1480   * <p>
1481   * Synchronization is implied on this method, because the locking mechanism
1482   * should be exclusive when this is called.
1483   */

1484  public void commit() throws TransactionException {
1485    // Are we currently allowed to commit/rollback?
1486
if (close_transaction_disabled) {
1487      throw new RuntimeException JavaDoc("Commit is not allowed.");
1488    }
1489    
1490    if (user != null) {
1491      user.refreshLastCommandTime();
1492    }
1493
1494    // NOTE, always connection exclusive op.
1495
getLockingMechanism().reset();
1496    tables_cache.clear();
1497
1498    if (transaction != null) {
1499      try {
1500
1501        // Close and commit the transaction
1502
transaction.closeAndCommit();
1503
1504        // Fire all SQL action level triggers that were generated on actions.
1505
database.getTriggerManager().flushTriggerEvents(trigger_event_list);
1506        
1507        
1508      }
1509      finally {
1510        // Dispose the current transaction
1511
disposeTransaction();
1512      }
1513    }
1514  }
1515
1516  /**
1517   * Rolls back the current transaction operating within this connection.
1518   * <p>
1519   * NOTE: It's guarenteed that the transaction will be closed even if an
1520   * exception occurs.
1521   * <p>
1522   * Synchronization is implied on this method, because the locking mechanism
1523   * should be exclusive when this is called.
1524   */

1525  public void rollback() {
1526    // Are we currently allowed to commit/rollback?
1527
if (close_transaction_disabled) {
1528      throw new RuntimeException JavaDoc("Rollback is not allowed.");
1529    }
1530
1531    if (user != null) {
1532      user.refreshLastCommandTime();
1533    }
1534
1535    // NOTE, always connection exclusive op.
1536
tables_cache.clear();
1537
1538    if (transaction != null) {
1539      getLockingMechanism().reset();
1540      try {
1541        transaction.closeAndRollback();
1542      }
1543      finally {
1544        // Dispose the current transaction
1545
disposeTransaction();
1546        // Dispose the jdbc connection
1547
if (jdbc_connection != null) {
1548          try {
1549            InternalJDBCHelper.disposeJDBCConnection(jdbc_connection);
1550          }
1551          catch (Throwable JavaDoc e) {
1552            Debug().write(Lvl.ERROR, this,
1553                          "Error disposing internal JDBC connection.");
1554            Debug().writeException(Lvl.ERROR, e);
1555            // We don't wrap this exception
1556
}
1557          jdbc_connection = null;
1558        }
1559      }
1560    }
1561  }
1562
1563  /**
1564   * Closes this database connection.
1565   */

1566  public void close() {
1567    try {
1568      rollback();
1569    }
1570    catch (Throwable JavaDoc e) {
1571      e.printStackTrace(System.err);
1572    }
1573    finally {
1574      if (table_backed_cache_list != null) {
1575        try {
1576          int sz = table_backed_cache_list.size();
1577          for (int i = 0; i < sz; ++i) {
1578            TableBackedCache cache =
1579                           (TableBackedCache) table_backed_cache_list.get(i);
1580            cache.detatchFrom(conglomerate);
1581          }
1582          table_backed_cache_list = null;
1583        }
1584        catch (Throwable JavaDoc e) {
1585          e.printStackTrace(System.err);
1586        }
1587      }
1588      // Remove any trigger listeners set for this connection,
1589
database.getTriggerManager().clearAllDatabaseConnectionTriggers(this);
1590    }
1591  }
1592
1593
1594  public void finalize() throws Throwable JavaDoc {
1595    super.finalize();
1596    close();
1597  }
1598
1599  // ---------- Inner classes ----------
1600

1601  /**
1602   * An implementation of ProcedureConnection generated from this object.
1603   */

1604  private class DCProcedureConnection implements ProcedureConnection {
1605    
1606    /**
1607     * The User of this connection before this procedure was started.
1608     */

1609    private User previous_user;
1610    
1611    /**
1612     * The 'close_transaction_disabled' flag when this connection was created.
1613     */

1614    private boolean transaction_disabled_flag;
1615
1616    /**
1617     * The JDBCConnection created by this object.
1618     */

1619    private java.sql.Connection JavaDoc jdbc_connection;
1620
1621
1622    public java.sql.Connection JavaDoc getJDBCConnection() {
1623      if (jdbc_connection == null) {
1624        jdbc_connection = InternalJDBCHelper.createJDBCConnection(getUser(),
1625                                                     DatabaseConnection.this);
1626      }
1627      return jdbc_connection;
1628    }
1629
1630    public Database getDatabase() {
1631      return DatabaseConnection.this.getDatabase();
1632    }
1633    
1634
1635    void dispose() {
1636      previous_user = null;
1637      if (jdbc_connection != null) {
1638        try {
1639          InternalJDBCHelper.disposeJDBCConnection(jdbc_connection);
1640        }
1641        catch (Throwable JavaDoc e) {
1642          Debug().write(Lvl.ERROR, this,
1643                        "Error disposing internal JDBC connection.");
1644          Debug().writeException(Lvl.ERROR, e);
1645          // We don't wrap this exception
1646
}
1647      }
1648    }
1649
1650  }
1651
1652  /**
1653   * An internal table info object that handles OLD and NEW tables for
1654   * triggered actions.
1655   */

1656  private class OldAndNewInternalTableInfo implements InternalTableInfo {
1657
1658    private boolean hasOLDTable() {
1659      return current_old_new_state.OLD_row_index != -1;
1660    }
1661    
1662    private boolean hasNEWTable() {
1663      return current_old_new_state.NEW_row_data != null;
1664    }
1665
1666    public int getTableCount() {
1667      int count = 0;
1668      if (hasOLDTable()) {
1669        ++count;
1670      }
1671      if (hasNEWTable()) {
1672        ++count;
1673      }
1674      return count;
1675    }
1676
1677    public int findTableName(TableName name) {
1678      if (hasOLDTable() && name.equals(Database.OLD_TRIGGER_TABLE)) {
1679        return 0;
1680      }
1681      if (hasNEWTable() && name.equals(Database.NEW_TRIGGER_TABLE)) {
1682        if (hasOLDTable()) {
1683          return 1;
1684        }
1685        else {
1686          return 0;
1687        }
1688      }
1689      return -1;
1690    }
1691
1692    public TableName getTableName(int i) {
1693      if (hasOLDTable()) {
1694        if (i == 0) {
1695          return Database.OLD_TRIGGER_TABLE;
1696        }
1697      }
1698      return Database.NEW_TRIGGER_TABLE;
1699    }
1700
1701    public boolean containsTableName(TableName name) {
1702      return findTableName(name) != -1;
1703    }
1704
1705    public String JavaDoc getTableType(int i) {
1706      return "SYSTEM TABLE";
1707    }
1708
1709    public DataTableDef getDataTableDef(int i) {
1710      DataTableDef table_def = DatabaseConnection.this.getDataTableDef(
1711                                        current_old_new_state.trigger_source);
1712      DataTableDef new_table_def = new DataTableDef(table_def);
1713      new_table_def.setTableName(getTableName(i));
1714      return new_table_def;
1715    }
1716
1717    public MutableTableDataSource createInternalTable(int index) {
1718      DataTableDef t_def = getDataTableDef(index);
1719
1720      TriggeredOldNewDataSource table =
1721                            new TriggeredOldNewDataSource(getSystem(), t_def);
1722
1723      if (hasOLDTable()) {
1724        if (index == 0) {
1725
1726          // Copy data from the table to the new table
1727
DataTable dtable = DatabaseConnection.this.getTable(
1728                                        current_old_new_state.trigger_source);
1729          RowData old_row_data = new RowData(table);
1730          int row_index = current_old_new_state.OLD_row_index;
1731          for (int i = 0; i < t_def.columnCount(); ++i) {
1732            old_row_data.setColumnDataFromTObject(i,
1733                                        dtable.getCellContents(i, row_index));
1734          }
1735          // All OLD tables are immutable
1736
table.setImmutable(true);
1737          table.setRowData(old_row_data);
1738          
1739          return table;
1740        }
1741      }
1742
1743      table.setImmutable(!current_old_new_state.mutable_NEW);
1744      table.setRowData(current_old_new_state.NEW_row_data);
1745
1746      return table;
1747    }
1748
1749  }
1750  
1751  /**
1752   * A MutableTableDataSource implementation that is used for trigger actions
1753   * to represent the data in the OLD and NEW tables.
1754   */

1755  private static class TriggeredOldNewDataSource extends GTDataSource {
1756
1757    private DataTableDef table_def;
1758
1759    private RowData content;
1760
1761    private boolean immutable;
1762    
1763    /**
1764     * Constructor.
1765     */

1766    public TriggeredOldNewDataSource(TransactionSystem system,
1767                                     DataTableDef table_def) {
1768      super(system);
1769      this.table_def = table_def;
1770    }
1771
1772    void setImmutable(boolean im) {
1773      this.immutable = im;
1774    }
1775
1776    void setRowData(RowData row_data) {
1777      this.content = row_data;
1778    }
1779    
1780    public DataTableDef getDataTableDef() {
1781      return table_def;
1782    }
1783    
1784    public int getRowCount() {
1785      return 1;
1786    }
1787    
1788    public TObject getCellContents(final int column, final int row) {
1789      if (row < 0 || row > 0) {
1790        throw new RuntimeException JavaDoc("Row index out of bounds.");
1791      }
1792      return content.getCellData(column);
1793    }
1794    
1795    public int addRow(RowData row_data) {
1796      throw new RuntimeException JavaDoc("Inserting into table '" +
1797                    getDataTableDef().getTableName() + "' is not permitted.");
1798    }
1799
1800    public void removeRow(int row_index) {
1801      throw new RuntimeException JavaDoc("Deleting from table '" +
1802                    getDataTableDef().getTableName() + "' is not permitted.");
1803    }
1804 
1805    public int updateRow(int row_index, RowData row_data) {
1806      if (immutable) {
1807        throw new RuntimeException JavaDoc("Updating table '" +
1808                    getDataTableDef().getTableName() + "' is not permitted.");
1809      }
1810      if (row_index < 0 || row_index > 0) {
1811        throw new RuntimeException JavaDoc("Row index out of bounds.");
1812      }
1813
1814      int sz = getDataTableDef().columnCount();
1815      for (int i = 0; i < sz; ++i) {
1816        content.setColumnDataFromTObject(i, row_data.getCellData(i));
1817      }
1818
1819      return 0;
1820    }
1821
1822    public MasterTableJournal getJournal() {
1823      // Shouldn't be used...
1824
throw new RuntimeException JavaDoc("Invalid method used.");
1825    }
1826
1827    public void flushIndexChanges() {
1828      // Shouldn't be used...
1829
throw new RuntimeException JavaDoc("Invalid method used.");
1830    }
1831  
1832    public void constraintIntegrityCheck() {
1833      // Should always pass (not integrity check needed for OLD/NEW table.
1834
}
1835
1836  }
1837
1838  /**
1839   * A list of DataTableDef system table definitions for tables internal to
1840   * the database connection.
1841   */

1842  private final static DataTableDef[] INTERNAL_DEF_LIST;
1843
1844  static {
1845    INTERNAL_DEF_LIST = new DataTableDef[5];
1846    INTERNAL_DEF_LIST[0] = GTStatisticsDataSource.DEF_DATA_TABLE_DEF;
1847    INTERNAL_DEF_LIST[1] = GTConnectionInfoDataSource.DEF_DATA_TABLE_DEF;
1848    INTERNAL_DEF_LIST[2] = GTCurrentConnectionsDataSource.DEF_DATA_TABLE_DEF;
1849    INTERNAL_DEF_LIST[3] = GTSQLTypeInfoDataSource.DEF_DATA_TABLE_DEF;
1850    INTERNAL_DEF_LIST[4] = GTPrivMapDataSource.DEF_DATA_TABLE_DEF;
1851  }
1852
1853  /**
1854   * An internal table info object that handles tables internal to a
1855   * DatabaseConnection object.
1856   */

1857  private class ConnectionInternalTableInfo extends AbstractInternalTableInfo {
1858
1859    /**
1860     * Constructor.
1861     */

1862    public ConnectionInternalTableInfo() {
1863      super("SYSTEM TABLE", INTERNAL_DEF_LIST);
1864    }
1865
1866    // ---------- Implemented ----------
1867

1868    public MutableTableDataSource createInternalTable(int index) {
1869      if (index == 0) {
1870        return new GTStatisticsDataSource(DatabaseConnection.this).init();
1871      }
1872      else if (index == 1) {
1873        return new GTConnectionInfoDataSource(DatabaseConnection.this).init();
1874      }
1875      else if (index == 2) {
1876        return new GTCurrentConnectionsDataSource(
1877                                              DatabaseConnection.this).init();
1878      }
1879      else if (index == 3) {
1880        return new GTSQLTypeInfoDataSource(DatabaseConnection.this).init();
1881      }
1882      else if (index == 4) {
1883        return new GTPrivMapDataSource(DatabaseConnection.this);
1884      }
1885      else {
1886        throw new RuntimeException JavaDoc();
1887      }
1888    }
1889
1890  }
1891
1892  /**
1893   * Call back interface for events that occur within the connection instance.
1894   */

1895  public static interface CallBack {
1896
1897    /**
1898     * Notifies the callee that a trigger event was fired that this user
1899     * is listening for.
1900     */

1901    void triggerNotify(String JavaDoc trigger_name, int trigger_event,
1902                       String JavaDoc trigger_source, int fire_count);
1903
1904  }
1905
1906  /**
1907   * An object that stores state about the trigger table OLD and NEW when
1908   * the connection is set up to execute a stored procedure.
1909   */

1910  static class OldNewTableState {
1911    
1912    /**
1913     * The name of the table that is the trigger source.
1914     */

1915    TableName trigger_source;
1916
1917    /**
1918     * The row index of the OLD data that is being updated or deleted in the
1919     * trigger source table.
1920     */

1921    int OLD_row_index = -1;
1922
1923    /**
1924     * The RowData of the new data that is being inserted/updated in the trigger
1925     * source table.
1926     */

1927    RowData NEW_row_data;
1928
1929    /**
1930     * If true then the 'new_data' information is mutable which would be true for
1931     * a BEFORE trigger. For example, we would want to change the data in the
1932     * row that caused the trigger to fire.
1933     */

1934    boolean mutable_NEW;
1935
1936    /**
1937     * The DataTable object that represents the OLD table, if set.
1938     */

1939    DataTable OLD_data_table;
1940
1941    /**
1942     * The DataTable object that represents the NEW table, if set.
1943     */

1944    DataTable NEW_data_table;
1945
1946    /**
1947     * Constructor.
1948     */

1949    OldNewTableState(TableName table_source,
1950                     int old_d, RowData new_d, boolean is_mutable) {
1951      this.trigger_source = table_source;
1952      this.OLD_row_index = old_d;
1953      this.NEW_row_data = new_d;
1954      this.mutable_NEW = is_mutable;
1955    }
1956
1957    /**
1958     * Default constructor.
1959     */

1960    OldNewTableState() {
1961    }
1962    
1963  }
1964  
1965}
1966
Popular Tags