KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > continuent > sequoia > controller > sql > schema > DatabaseSchema


1 /**
2  * Sequoia: Database clustering technology.
3  * Copyright (C) 2002-2004 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
6  * Copyright (C) 2005-2006 Continuent, Inc.
7  * Contact: sequoia@continuent.org
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  * Initial developer(s): Emmanuel Cecchet.
22  * Contributor(s): Heiko Schramm.
23  */

24
25 package org.continuent.sequoia.controller.sql.schema;
26
27 import java.io.Serializable JavaDoc;
28 import java.sql.SQLException JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.ConcurrentModificationException JavaDoc;
31 import java.util.HashMap JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.List JavaDoc;
34 import java.util.SortedSet JavaDoc;
35
36 import org.continuent.sequoia.common.i18n.Translate;
37 import org.continuent.sequoia.common.log.Trace;
38 import org.continuent.sequoia.controller.backend.DatabaseBackend;
39 import org.continuent.sequoia.controller.locks.TransactionLogicalLock;
40 import org.continuent.sequoia.controller.requestmanager.RequestManager;
41 import org.continuent.sequoia.controller.requestmanager.TransactionMetaData;
42 import org.continuent.sequoia.controller.requests.AbstractRequest;
43 import org.continuent.sequoia.controller.requests.AbstractWriteRequest;
44 import org.continuent.sequoia.controller.requests.AlterRequest;
45 import org.continuent.sequoia.controller.requests.CreateRequest;
46 import org.continuent.sequoia.controller.requests.DropRequest;
47 import org.continuent.sequoia.controller.requests.ParsingGranularities;
48 import org.continuent.sequoia.controller.semantic.SemanticBehavior;
49
50 /**
51  * A <code>DatabaseSchema</code> describes all the tables and columns of a
52  * database.
53  *
54  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
55  * @author <a HREF="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
56  * @version 1.0
57  */

58 public class DatabaseSchema implements Serializable JavaDoc
59 {
60   private static final long serialVersionUID = 1105453274994163661L;
61
62   /**
63    * Virtual database name with a dot at the end (e.g. 'mydb.') that can be used
64    * as a prefix in the table names
65    */

66   private String JavaDoc vdbNameWithDot;
67   /** <code>HashMap</code> of <code>DatabaseTables</code>. */
68   private HashMap JavaDoc tables;
69   /** <code>HashMap</code> of <code>DatabaseProcedures</code>. */
70   private HashMap JavaDoc procedures;
71
72   /** Lock for this schema */
73   private transient TransactionLogicalLock lock = new TransactionLogicalLock();
74
75   /**
76    * Creates a new <code>DatabaseSchema</code> instance.
77    *
78    * @param vdbName the virtual database name this schema represents
79    */

80   public DatabaseSchema(String JavaDoc vdbName)
81   {
82     this.vdbNameWithDot = vdbName + ".";
83     tables = new HashMap JavaDoc();
84     procedures = new HashMap JavaDoc();
85   }
86
87   /**
88    * Creates a new <code>DatabaseSchema</code> instance with a specified
89    * number of tables.
90    *
91    * @param vdbName the virtual database name this schema represents
92    * @param nbOfTables an <code>int</code> value
93    */

94   public DatabaseSchema(String JavaDoc vdbName, int nbOfTables)
95   {
96     this.vdbNameWithDot = vdbName + ".";
97     tables = new HashMap JavaDoc(nbOfTables);
98     procedures = new HashMap JavaDoc();
99   }
100
101   /**
102    * Creates a new <code>DatabaseSchema</code> instance from an existing
103    * database schema (the schema is cloned).
104    *
105    * @param schema the existing database schema
106    */

107   public DatabaseSchema(DatabaseSchema schema)
108   {
109     if (schema == null)
110       throw new IllegalArgumentException JavaDoc(
111           "Illegal null database schema in DatabaseSchema(DatabaseSchema) constructor");
112
113     vdbNameWithDot = schema.getVirtualDatabaseName();
114     tables = new HashMap JavaDoc(schema.getTables());
115     procedures = new HashMap JavaDoc(schema.getProcedures());
116   }
117
118   /**
119    * Adds a <code>DatabaseTable</code> describing a table of the database.
120    *
121    * @param table the table to add
122    */

123   public synchronized void addTable(DatabaseTable table)
124   {
125     if (table == null)
126       throw new IllegalArgumentException JavaDoc(
127           "Illegal null database table in addTable(DatabaseTable) method");
128     tables.put(table.getName(), table);
129     if (table.getSchema() != null)
130       tables.put(table.getSchema() + "." + table.getName(), table);
131   }
132
133   /**
134    * Adds a <code>DatabaseProcedure</code> describing a procedure of the
135    * database.
136    *
137    * @param procedure the procedure to add
138    */

139   public synchronized void addProcedure(DatabaseProcedure procedure)
140   {
141     if (procedure == null)
142       throw new IllegalArgumentException JavaDoc(
143           "Illegal null database table in addTable(DatabaseTable) method");
144     String JavaDoc key = procedure.getKey();
145     int semicolon = key.indexOf(';');
146     if (semicolon > 0)
147     { // The procedure includes a version number (like in Sybase). If key is
148
// myproc;1.4(2), also add myproc(2) in the list
149
String JavaDoc keyWithoutVersionNumber = key.substring(0, semicolon)
150           + key.substring(procedure.getName().length());
151       procedures.put(keyWithoutVersionNumber, procedure);
152     }
153     procedures.put(key, procedure);
154   }
155
156   /**
157    * Returns true if all tables are not locked by anyone.
158    *
159    * @param request request trying to execute (to indicate if it is in
160    * autocommit mode and the transaction id)
161    * @return true if all tables are unlocked.
162    */

163   public boolean allTablesAreUnlockedOrLockedByTransaction(
164       AbstractRequest request)
165   {
166     // Optimistic approach where we use an iterator and if the tables are
167
// modified concurrently, iter.next() will fail and we will restart the
168
// search.
169
boolean retry;
170     do
171     {
172       retry = false;
173       try
174       {
175         for (Iterator JavaDoc iter = tables.values().iterator(); iter.hasNext();)
176         {
177           TransactionLogicalLock l = ((DatabaseTable) iter.next()).getLock();
178           if (l.isLocked())
179           {
180             // If the lock is held by another transaction then this is not ok
181
if (request.getTransactionId() != l.getLocker())
182               return false;
183           }
184         }
185       }
186       catch (ConcurrentModificationException JavaDoc e)
187       {
188         retry = true;
189       }
190     }
191     while (retry);
192     return true;
193   }
194
195   /**
196    * Lock all tables that are not already locked by this transaction (assumes
197    * that locks are free).
198    *
199    * @param request request trying to execute (to indicate if it is in
200    * autocommit mode and the transaction id)
201    * @return list of locks acquired (excluding locks already acquired before
202    * this method was called)
203    */

204   public List JavaDoc lockAllTables(AbstractRequest request)
205   {
206     // Optimistic approach where we use an iterator and if the tables are
207
// modified concurrently, iter.next() will fail and we will restart the
208
// search.
209
boolean retry;
210     List JavaDoc acquiredLocks = new ArrayList JavaDoc();
211     do
212     {
213       retry = false;
214       try
215       {
216         for (Iterator JavaDoc iter = tables.values().iterator(); iter.hasNext();)
217         {
218           TransactionLogicalLock l = ((DatabaseTable) iter.next()).getLock();
219           if (!l.isLocked())
220           {
221             l.acquire(request);
222             acquiredLocks.add(l);
223           }
224         }
225       }
226       catch (ConcurrentModificationException JavaDoc e)
227       {
228         retry = true;
229       }
230     }
231     while (retry);
232     return acquiredLocks;
233   }
234
235   /**
236    * Returns the lock for this table.
237    *
238    * @return a <code>TransactionLogicalLock</code> instance
239    */

240   public TransactionLogicalLock getLock()
241   {
242     return lock;
243   }
244
245   /**
246    * When the database schema is reloaded, the locks held by active transactions
247    * must be retained.
248    *
249    * @param oldSchema the previous version of the schema.
250    */

251   public void setLocks(DatabaseSchema oldSchema)
252   {
253     lock = oldSchema.lock;
254     for (Iterator JavaDoc iter = tables.values().iterator(); iter.hasNext();)
255     {
256       DatabaseTable table = (DatabaseTable) iter.next();
257       DatabaseTable oldTable = oldSchema.getTable(table.getName(), true);
258       if (oldTable != null)
259         table.setLock(oldTable);
260     }
261   }
262
263   /**
264    * Returns the <code>DatabaseProcedure</code> object matching the given
265    * procedure name or <code>null</code> if not found.
266    *
267    * @param procedureKey the procedure key to look for
268    * @return a <code>DatabaseProcedure</code> value or null
269    */

270   public DatabaseProcedure getProcedure(String JavaDoc procedureKey)
271   {
272     DatabaseProcedure proc = null;
273     if (procedureKey == null)
274       return null;
275
276     synchronized (procedures)
277     {
278       proc = (DatabaseProcedure) procedures.get(procedureKey);
279       // If we don't find the stored procedure in the procedures hashmap using
280
// case sensitive matching, we try to find it in lower case.
281
if (proc == null)
282         proc = (DatabaseProcedure) procedures.get(procedureKey.toLowerCase());
283       return proc;
284     }
285   }
286
287   /**
288    * Returns the <code>DatabaseProcedure</code> object matching the given
289    * procedure or <code>null</code> if not found.
290    *
291    * @param procedure the procedure to look for
292    * @return a <code>DatabaseProcedure</code> value or null
293    */

294   public DatabaseProcedure getProcedure(DatabaseProcedure procedure)
295   {
296     if (procedure == null)
297       return null;
298
299     // Optimistic approach where we use an iterator and if the tables are
300
// modified concurrently, iter.next() will fail and we will restart the
301
// search.
302
boolean retry;
303     do
304     {
305       retry = false;
306       try
307       {
308         for (Iterator JavaDoc iter = procedures.values().iterator(); iter.hasNext();)
309         {
310           DatabaseProcedure p = (DatabaseProcedure) iter.next();
311           if (procedure.equals(p))
312             return p;
313         }
314       }
315       catch (ConcurrentModificationException JavaDoc e)
316       {
317         retry = true;
318       }
319     }
320     while (retry);
321     return null;
322   }
323
324   /**
325    * Returns an <code>HashMap</code> of <code>DatabaseProcedure</code>
326    * objects describing the database. The key entry is given by
327    * DatabaseProcedure.getKey().
328    *
329    * @return an <code>HashMap</code> of <code>DatabaseProcedure</code>
330    */

331   public HashMap JavaDoc getProcedures()
332   {
333     return procedures;
334   }
335
336   /**
337    * Returns the <code>DatabaseTable</code> object matching the given table
338    * name or <code>null</code> if not found.
339    *
340    * @param tableName the table name to look for
341    * @return a <code>DatabaseTable</code> value or null
342    */

343   public DatabaseTable getTable(String JavaDoc tableName)
344   {
345     if ((tableName == null) || (tableName.length() == 0))
346       return null;
347
348     synchronized (tables)
349     {
350       DatabaseTable t = (DatabaseTable) tables.get(tableName);
351       if (t == null)
352       {
353         // Check if we have a fully qualified table name prefixed by the
354
// database name (e.g. mydb.table1).
355
if (tableName.startsWith(vdbNameWithDot))
356         { // Strip the (virtual) database prefix if any
357
t = (DatabaseTable) tables.get(tableName.substring(vdbNameWithDot
358               .length()));
359         }
360         // SEQUOIA-518: (Database specific) use of quoted tableNames:
361
// PostreSQL accepts '"', MySQL accepts '`'
362
// so we need to trim them
363
else
364         {
365           char firstChar = tableName.charAt(0);
366           if (firstChar == '\"' || firstChar == '`' || firstChar == '\'')
367           {
368             String JavaDoc trimedTableName = tableName.substring(1,
369                 tableName.length() - 1);
370             t = (DatabaseTable) tables.get(trimedTableName);
371           }
372         }
373       }
374       return t;
375     }
376   }
377
378   private DatabaseTable getTable(DatabaseTable other)
379   {
380     if (other.getSchema() != null)
381       return getTable(other.getSchema() + "." + other.getName());
382     else
383       return getTable(other.getName());
384   }
385
386   /**
387    * Returns the <code>DatabaseTable</code> object matching the given table
388    * name or <code>null</code> if not found. An extra boolean indicates if
389    * table name matching is case sensitive or not.
390    *
391    * @param tableName the table name to look for
392    * @param isCaseSensitive true if name matching must be case sensitive
393    * @return a <code>DatabaseTable</code> value or null
394    */

395   public DatabaseTable getTable(String JavaDoc tableName, boolean isCaseSensitive)
396   {
397     if ((tableName == null) || (tableName.length() == 0))
398       return null;
399
400     DatabaseTable t = getTable(tableName);
401     if (isCaseSensitive || (t != null))
402       return t;
403
404     // Not found with the case sensitive approach, let's try a case insensitive
405
// way
406

407     // Strip the (virtual) database prefix if any for fully qualified table
408
// names
409
if (tableName.startsWith(vdbNameWithDot))
410       tableName = tableName.substring(vdbNameWithDot.length());
411
412     // Optimistic approach where we use an iterator and if the tables are
413
// modified concurrently, iter.next() will fail and we will restart the
414
// search.
415
boolean retry;
416     do
417     {
418       retry = false;
419       try
420       {
421         for (Iterator JavaDoc iter = tables.values().iterator(); iter.hasNext();)
422         {
423           t = (DatabaseTable) iter.next();
424           if (tableName.equalsIgnoreCase(t.getName()))
425             return t;
426         }
427       }
428       catch (ConcurrentModificationException JavaDoc e)
429       {
430         retry = true;
431       }
432     }
433     while (retry);
434     return null;
435   }
436
437   /**
438    * Returns an <code>HashMap</code> of <code>DatabaseTable</code> objects
439    * describing the database. The key is the table name.
440    *
441    * @return an <code>HashMap</code> of <code>DatabaseTable</code>
442    */

443   public HashMap JavaDoc getTables()
444   {
445     return tables;
446   }
447
448   /**
449    * Returns the virtual database name value.
450    *
451    * @return Returns the virtual database name.
452    */

453   public final String JavaDoc getVirtualDatabaseName()
454   {
455     return vdbNameWithDot;
456   }
457
458   /**
459    * Returns <code>true</code> if the given <code>ProcedureName</code> is
460    * found in this schema.
461    *
462    * @param procedureName the name of the procedure you are looking for
463    * @param nbOfParameters number of parameters of the stored procecdure
464    * @return <code>true</code> if the procedure has been found
465    */

466   public boolean hasProcedure(String JavaDoc procedureName, int nbOfParameters)
467   {
468     return procedures.containsKey(DatabaseProcedure.buildKey(procedureName,
469         nbOfParameters));
470   }
471
472   /**
473    * Returns true if the given transaction locks (or wait for a lock) on any of
474    * the table of this schema.
475    *
476    * @param transactionId the transaction identifier
477    * @return true if the transaction locks or wait for a lock on at least one
478    * table
479    */

480   public boolean hasATableLockedByTransaction(long transactionId)
481   {
482     // Optimistic approach where we use an iterator and if the tables are
483
// modified concurrently, iter.next() will fail and we will restart the
484
// search.
485
boolean retry;
486     do
487     {
488       retry = false;
489       try
490       {
491         for (Iterator JavaDoc iter = tables.values().iterator(); iter.hasNext();)
492         {
493           TransactionLogicalLock l = ((DatabaseTable) iter.next()).getLock();
494           if (l.isLocked()
495               && ((l.getLocker() == transactionId) || (l
496                   .isWaiting(transactionId))))
497             return true;
498         }
499       }
500       catch (ConcurrentModificationException JavaDoc e)
501       {
502         retry = true;
503       }
504     }
505     while (retry);
506     return false;
507   }
508
509   /**
510    * Returns <code>true</code> if the given <code>TableName</code> is found
511    * in this schema.
512    *
513    * @param tableName the name of the table you are looking for
514    * @return <code>true</code> if the table has been found
515    */

516   public boolean hasTable(String JavaDoc tableName)
517   {
518     // Optimistic approach, if the tables are modified concurrently we will fail
519
// and we will restart the search.
520
do
521     {
522       try
523       {
524         return tables.containsKey(tableName);
525       }
526       catch (ConcurrentModificationException JavaDoc e)
527       {
528       }
529     }
530     while (true);
531   }
532
533   /**
534    * Checks if this <code>DatabaseSchema</code> is a compatible subset of a
535    * given schema. It means that all tables in this schema must be present with
536    * the same definition in the other schema.
537    *
538    * @param other the object to compare with
539    * @return <code>true</code> if the two schemas are compatible
540    */

541   public boolean isCompatibleSubset(DatabaseSchema other)
542   {
543     if (other == null)
544       return false;
545
546     DatabaseTable table, otherTable;
547     for (Iterator JavaDoc iter = tables.values().iterator(); iter.hasNext();)
548     { // Parse all tables
549
table = (DatabaseTable) iter.next();
550       otherTable = other.getTable(table);
551       if (otherTable == null)
552         return false; // Not present
553
else if (!table.equalsIgnoreType(otherTable))
554         return false; // Not compatible
555
}
556     DatabaseProcedure procedure, otherProcedure;
557     for (Iterator JavaDoc iter = procedures.values().iterator(); iter.hasNext();)
558     { // Parse all procedures
559
procedure = (DatabaseProcedure) iter.next();
560       otherProcedure = other.getProcedure(procedure.getName());
561       if (otherProcedure == null)
562         return false; // Not present
563
else if (!procedure.equals(otherProcedure))
564         return false; // Not compatible
565
}
566     return true; // Ok, all tables passed the test
567
}
568
569   /**
570    * Checks if this <code>DatabaseSchema</code> is compatible with the given
571    * schema. It means that all tables in this schema that are common with the
572    * other schema must be identical.
573    *
574    * @param other the object to compare with
575    * @return <code>true</code> if the two schemas are compatible
576    */

577   public boolean isCompatibleWith(DatabaseSchema other)
578   {
579     DatabaseTable table, otherTable;
580     for (Iterator JavaDoc iter = tables.values().iterator(); iter.hasNext();)
581     { // Parse all tables
582
table = (DatabaseTable) iter.next();
583       otherTable = other.getTable(table);
584       if (otherTable == null)
585         continue; // Not present in other schema
586
else if (!table.equalsIgnoreType(otherTable))
587         return false; // Not compatible
588
}
589     DatabaseProcedure procedure, otherProcedure;
590     for (Iterator JavaDoc iter = procedures.values().iterator(); iter.hasNext();)
591     { // Parse all procedures
592
procedure = (DatabaseProcedure) iter.next();
593       otherProcedure = other.getProcedure(procedure.getName());
594       if (otherProcedure == null)
595         continue; // Not present
596
else if (!procedure.equals(otherProcedure))
597         return false; // Not compatible
598
}
599     return true; // Ok, all tables passed the test
600
}
601
602   /**
603    * Merges the given schema with the current one. All missing tables or columns
604    * are added if no conflict is detected. An exception is thrown if the given
605    * schema definition conflicts with the current one.
606    *
607    * @param databaseSchema the schema to merge
608    * @throws SQLException if the schemas conflict
609    */

610   public void mergeSchema(DatabaseSchema databaseSchema) throws SQLException JavaDoc
611   {
612     if (databaseSchema == null)
613       throw new IllegalArgumentException JavaDoc(
614           "Illegal null database schema in mergeSchema(DatabaseSchema) method");
615
616     HashMap JavaDoc otherTables = databaseSchema.getTables();
617     if ((otherTables == null) || (otherTables.size() == 0))
618       return;
619
620     DatabaseTable table, originalTable;
621     for (Iterator JavaDoc iter = otherTables.values().iterator(); iter.hasNext();)
622     { // Parse all tables
623
table = (DatabaseTable) iter.next();
624       originalTable = getTable(table);
625       if (originalTable == null)
626         addTable(table);
627       else
628         originalTable.merge(table);
629     }
630
631     HashMap JavaDoc otherProcedures = databaseSchema.getProcedures();
632     if ((otherProcedures == null) || (otherProcedures.size() == 0))
633       return;
634
635     DatabaseProcedure procedure, originalProcedure;
636     for (Iterator JavaDoc iter = otherProcedures.values().iterator(); iter.hasNext();)
637     { // Parse all procedures
638
procedure = (DatabaseProcedure) iter.next();
639       originalProcedure = getProcedure(procedure);
640       if (originalProcedure == null)
641         addProcedure(procedure);
642     }
643   }
644
645   /**
646    * Release locks held by the given transaction on all tables.
647    *
648    * @param transactionId the transaction identifier
649    */

650   public void releaseLocksOnAllTables(long transactionId)
651   {
652     // Release global lock
653
lock.release(transactionId);
654
655     // Optimistic approach where we use an iterator and if the tables are
656
// modified concurrently, iter.next() will fail and we will restart the
657
// search.
658
boolean retry;
659     do
660     {
661       retry = false;
662       try
663       {
664         for (Iterator JavaDoc iter = tables.values().iterator(); iter.hasNext();)
665         {
666           TransactionLogicalLock l = ((DatabaseTable) iter.next()).getLock();
667           l.release(transactionId);
668         }
669       }
670       catch (ConcurrentModificationException JavaDoc e)
671       {
672         retry = true;
673       }
674     }
675     while (retry);
676   }
677
678   /**
679    * removes a <code>DatabaseProcedure</code> describing a procedure of the
680    * database.
681    *
682    * @param procedure to remove
683    * @return true if the procedure was successfully removed
684    */

685   public synchronized boolean removeProcedure(DatabaseProcedure procedure)
686   {
687     if (procedure == null)
688       throw new IllegalArgumentException JavaDoc(
689           "Illegal null database procedure in removeProcedure(DatabaseProcedure) method");
690     return procedures.remove(procedure.getKey()) != null;
691   }
692
693   /**
694    * Removes a <code>DatabaseTable</code> describing a table of the database.
695    *
696    * @param table the table to remove
697    * @return true if the table was successfully removed
698    */

699   public synchronized boolean removeTable(DatabaseTable table)
700   {
701     if (table == null)
702       throw new IllegalArgumentException JavaDoc(
703           "Illegal null database table in removeTable(DatabaseTable) method");
704     if (table.getSchema() != null)
705       return ((tables.remove(table.getName()) != null) && (tables.remove(table
706           .getSchema()
707           + "." + table.getName()) != null));
708     else
709       return (tables.remove(table.getName()) != null);
710   }
711
712   /**
713    * Updates the given schema with the current one. All missing tables or
714    * columns are added and if the given schema definition conflicts with the
715    * current one, the current schema definition is overriden with the one that
716    * is provided.
717    *
718    * @param databaseSchema the schema to merge
719    */

720   public void updateSchema(DatabaseSchema databaseSchema)
721   {
722     if (databaseSchema == null)
723       throw new IllegalArgumentException JavaDoc(
724           "Illegal null database schema in mergeSchema(DatabaseSchema) method");
725
726     HashMap JavaDoc otherTables = databaseSchema.getTables();
727
728     // Remove tables that do not exist anymore in new schema
729
for (Iterator JavaDoc iter = tables.values().iterator(); iter.hasNext();)
730     {
731       DatabaseTable t = (DatabaseTable) iter.next();
732       if (!databaseSchema.hasTable(t.getSchema() + "." + t.getName()))
733         iter.remove();
734     }
735
736     // Add missing tables
737
DatabaseTable table, originalTable;
738     for (Iterator JavaDoc iter = otherTables.values().iterator(); iter.hasNext();)
739     {
740       table = (DatabaseTable) iter.next();
741       originalTable = getTable(table);
742       if (originalTable == null)
743         addTable(table);
744       else
745         originalTable.updateColumns(table);
746     }
747
748     // Just replace the stored procedures
749
this.procedures = databaseSchema.getProcedures();
750   }
751
752   /**
753    * Two <code>DatabaseSchema</code> are considered equal if they have the
754    * same tables and the same procedures.
755    *
756    * @param other the object to compare with
757    * @return <code>true</code> if the schemas are equals
758    */

759   public boolean equals(Object JavaDoc other)
760   {
761     boolean equal = true;
762     if ((other == null) || !(other instanceof DatabaseSchema))
763       return false;
764     if (tables == null)
765       equal &= ((DatabaseSchema) other).getTables() == null;
766     else
767       equal &= tables.equals(((DatabaseSchema) other).getTables());
768     if (procedures == null)
769       equal &= ((DatabaseSchema) other).getProcedures() == null;
770     else
771       equal &= procedures.equals(((DatabaseSchema) other).getProcedures());
772     return equal;
773   }
774
775   /**
776    * Update the schema if needed by the given request. This method will update
777    * either the schema of the RequestManager or the schema of the
778    * DatabaseBackend.
779    *
780    * @param request Request that is being executed
781    * @param logger Logger that has to be used when logging
782    * @param rm RequestManager for which we may have to update the schema
783    * @param tm TransactionMetadata when called by the RequestManager if the
784    * request executes in a transaction, null otherwise (when non
785    * transactionnal or called from the DatabaseBackend)
786    * @param dbBackend DatabaseBackend for which the schema is going to be
787    * updated, null if the schema to be updated is the on of the
788    * RequestManager
789    */

790   public void updateDatabaseSchema(AbstractWriteRequest request, Trace logger,
791       RequestManager rm, TransactionMetaData tm, DatabaseBackend dbBackend)
792   {
793     if (request.altersSomething())
794     {
795       SemanticBehavior semantic = request.getSemantic();
796       if (semantic == null)
797       {
798         // Sanity check, should never happen
799
logger.error("No semantic information found for request '"
800             + request.getSqlShortForm(rm.getVirtualDatabase()
801                 .getSqlShortFormLength()) + "'. Schema cannot be updated.");
802
803         return;
804       }
805
806       // Use semantic information
807
// Update the schema if needed
808
if (semantic.altersDatabaseSchema())
809       {
810         if (request.isCreate())
811         { // Add the table to the schema
812
CreateRequest createRequest = (CreateRequest) request;
813           if (createRequest.getDatabaseTable() != null)
814           {
815             addTable(new DatabaseTable(((CreateRequest) request)
816                 .getDatabaseTable()));
817             if (logger.isDebugEnabled())
818               logger.debug(Translate.get("requestmanager.schema.add.table",
819                   request.getTableName()));
820             // TODO : right now, we ask a complete schema refresh
821
// Optimization, we should refresh only this table
822
setSchemaIsDirty(true, request, rm, dbBackend);
823           }
824           else
825             // Some other create statement that modifies the schema, force
826
// refresh
827
setSchemaIsDirty(true, request, rm, dbBackend);
828         }
829         else if (request.isDrop())
830         { // Delete the table from the schema
831
SortedSet JavaDoc tablesToRemove = ((DropRequest) request).getTablesToDrop();
832
833           if ((tablesToRemove != null))
834           {
835             for (Iterator JavaDoc iter = tablesToRemove.iterator(); iter.hasNext();)
836             {
837               String JavaDoc tableToRemove = (String JavaDoc) iter.next();
838               DatabaseTable t = getTable(tableToRemove);
839               if (t != null)
840               {
841                 // Remove table from schema
842
removeTable(t);
843                 if (logger.isDebugEnabled())
844                   logger.debug("Removed table '" + t.getName()
845                       + "' from schema");
846
847                 // Remove table from dependending tables
848
Iterator JavaDoc keys = getTables().keySet().iterator();
849                 while (keys.hasNext())
850                 {
851                   String JavaDoc tableName = (String JavaDoc) keys.next();
852                   DatabaseTable table = getTable(tableName);
853                   if (table.getDependingTables() != null)
854                   {
855                     table.getDependingTables().remove(t.getName());
856                     if (logger.isDebugEnabled())
857                       logger.debug("Removed table '" + t.getName()
858                           + "' from dependending tables list of table '"
859                           + table.getName() + "' in schema");
860                   }
861                 }
862               }
863               else
864               {
865                 // Unsupported force re-fetch from db
866
setSchemaIsDirty(true, request, rm, dbBackend);
867                 return;
868               }
869             }
870           }
871         }
872         else if (request.isAlter()
873             && (rm.getRequiredParsingGranularity() > ParsingGranularities.TABLE))
874         { // Add or drop the column from the table
875
AlterRequest req = (AlterRequest) request;
876           DatabaseTable alteredTable = getTable(req.getTableName());
877           if ((alteredTable != null) && (req.getColumn() != null))
878           {
879             if (req.isDrop())
880               alteredTable.removeColumn(req.getColumn().getName());
881             else if (req.isAdd())
882               alteredTable.addColumn(req.getColumn());
883             else
884               // Unsupported, force refresh
885
setSchemaIsDirty(true, request, rm, dbBackend);
886           }
887           else
888             // Table not found, force refresh
889
setSchemaIsDirty(true, request, rm, dbBackend);
890         }
891         else
892           // Unsupported, force refresh
893
setSchemaIsDirty(true, request, rm, dbBackend);
894       }
895     }
896   }
897
898   /**
899    * Set the right schema to dirty (either RequestManager or DatabaseBackend
900    * schema), forcing a schema refresh.
901    *
902    * @param schemaIsDirty True if the schema is dirty and needs to be refreshed
903    * @param request The request that is at the origin of the schema refresh
904    * @param rm RequestManager for which we may have to update the schema
905    * @param dbBackend DatabaseBackend for which the schema is going to be
906    * updated, null if the schema to be updated is RequestManager one
907    */

908   private void setSchemaIsDirty(boolean schemaIsDirty, AbstractRequest request,
909       RequestManager rm, DatabaseBackend dbBackend)
910   {
911     if (dbBackend != null)
912       dbBackend.setSchemaIsDirty(schemaIsDirty, request);
913     else
914       rm.setSchemaIsDirty(schemaIsDirty);
915   }
916
917 }
Popular Tags