KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > continuent > sequoia > controller > requests > DeleteRequest


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): Julie Marguerite, Mathieu Peltier, Sara Bouchenak.
23  */

24
25 package org.continuent.sequoia.controller.requests;
26
27 import java.io.Serializable JavaDoc;
28 import java.sql.SQLException JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.SortedSet JavaDoc;
31 import java.util.StringTokenizer JavaDoc;
32 import java.util.TreeSet JavaDoc;
33
34 import org.continuent.sequoia.common.i18n.Translate;
35 import org.continuent.sequoia.controller.semantic.SemanticBehavior;
36 import org.continuent.sequoia.controller.sql.schema.AliasedDatabaseTable;
37 import org.continuent.sequoia.controller.sql.schema.DatabaseColumn;
38 import org.continuent.sequoia.controller.sql.schema.DatabaseSchema;
39 import org.continuent.sequoia.controller.sql.schema.DatabaseTable;
40 import org.continuent.sequoia.controller.sql.schema.TableColumn;
41
42 /**
43  * An <code>DeleteRequest</code> is an SQL request with the following syntax:
44  *
45  * <pre>DELETE [table1] FROM table1,table2,table3,... WHERE search-condition
46  * or DELETE t WHERE search-condition
47  * </pre>
48  *
49  * Note that DELETE from multiple tables are not supported but this is not part
50  * of the SQL standard.
51  *
52  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
53  * @author <a HREF="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
54  * @author <a HREF="mailto:Mathieu.Peltier@inrialpes.fr">Mathieu Peltier </a>
55  * @author <a HREF="mailto:Sara.Bouchenak@epfl.ch">Sara Bouchenak </a>
56  * @version 1.0
57  */

58 public class DeleteRequest extends AbstractWriteRequest implements Serializable JavaDoc
59 {
60   private static final long serialVersionUID = 2321073659092135758L;
61
62   /** <code>true</code> if this query only deletes a single row. */
63   protected transient boolean isUnique = false;
64
65   /** <code>ArrayList</code> of <code>String</code> objects */
66   protected transient ArrayList JavaDoc from;
67
68   /**
69    * <code>ArrayList</code> of values <code>String</code> associated with
70    * the unique columns involved in this delete query.
71    * <p>
72    * The <code>values</code> instance variable is only used when a <code>
73    * COLUMN_UNIQUE_DELETE</code>
74    * granularity is applied. Here, the DELETE request is UNIQUE: all columns of
75    * the WHERE clause are UNIQUE and used in the left part of an equality. When
76    * such a granularity is used, the <code>columns</code> instance variable
77    * contains only UNIQUE columns.
78    *
79    * @see org.continuent.sequoia.controller.cache.result.CachingGranularities
80    */

81   protected ArrayList JavaDoc whereValues;
82
83   /**
84    * Creates a new <code>DeleteRequest</code> instance. The caller must give
85    * an SQL request, without any leading or trailing spaces and beginning with
86    * 'delete ' (it will not be checked).
87    * <p>
88    * The request is not parsed but it can be done later by a call to
89    * {@link #parse(DatabaseSchema, int, boolean)}.
90    *
91    * @param sqlQuery the SQL request
92    * @param escapeProcessing should the driver to escape processing before
93    * sending to the database ?
94    * @param timeout an <code>int</code> value
95    * @param lineSeparator the line separator used in the query
96    * @see #parse
97    */

98   public DeleteRequest(String JavaDoc sqlQuery, boolean escapeProcessing, int timeout,
99       String JavaDoc lineSeparator)
100   {
101     super(sqlQuery, escapeProcessing, timeout, lineSeparator,
102         RequestType.DELETE);
103   }
104
105   /**
106    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersAggregateList()
107    */

108   public boolean altersAggregateList()
109   {
110     return false;
111   }
112
113   /**
114    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersDatabaseCatalog()
115    */

116   public boolean altersDatabaseCatalog()
117   {
118     return false;
119   }
120
121   /**
122    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersDatabaseSchema()
123    */

124   public boolean altersDatabaseSchema()
125   {
126     return false;
127   }
128
129   /**
130    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersMetadataCache()
131    */

132   public boolean altersMetadataCache()
133   {
134     return false;
135   }
136
137   /**
138    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersQueryResultCache()
139    */

140   public boolean altersQueryResultCache()
141   {
142     return true;
143   }
144
145   /**
146    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersSomething()
147    */

148   public boolean altersSomething()
149   {
150     return true;
151   }
152
153   /**
154    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersStoredProcedureList()
155    */

156   public boolean altersStoredProcedureList()
157   {
158     return false;
159   }
160
161   /**
162    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersUserDefinedTypes()
163    */

164   public boolean altersUserDefinedTypes()
165   {
166     return false;
167   }
168
169   /**
170    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersUsers()
171    */

172   public boolean altersUsers()
173   {
174     return false;
175   }
176
177   /**
178    * @see AbstractRequest#cloneParsing(AbstractRequest)
179    */

180   public void cloneParsing(AbstractRequest request)
181   {
182     if (!request.isParsed())
183       return;
184     cloneParsingCommons(request);
185     cloneTableNameAndColumns((AbstractWriteRequest) request);
186     isParsed = true;
187   }
188
189   /**
190    * Extracts the tables from the given <code>FROM</code> clause and retrieves
191    * their alias if any.
192    *
193    * @param fromClause the <code>FROM</code> clause of the request (without
194    * the <code>FROM</code> keyword)
195    * @param dbs the <code>DatabaseSchema</code> this request refers to
196    * @return an <code>ArrayList</code> of <code>AliasedDatabaseTable</code>
197    * objects
198    * @exception an <code>SQLException</code> if an error occurs
199    */

200   private ArrayList JavaDoc getFromTables(String JavaDoc fromClause, DatabaseSchema dbs)
201       throws SQLException JavaDoc
202   {
203     StringTokenizer JavaDoc tables = new StringTokenizer JavaDoc(fromClause, ",");
204     ArrayList JavaDoc result = new ArrayList JavaDoc(tables.countTokens());
205     while (tables.hasMoreTokens())
206     {
207       String JavaDoc dropTableName = tables.nextToken().trim();
208       // Check if the table has an alias
209
// Example: SELECT x.price FROM item x
210
String JavaDoc alias = null;
211       int aliasIdx = dropTableName.indexOf(' ');
212       if (aliasIdx != -1)
213       {
214         alias = dropTableName.substring(aliasIdx);
215         dropTableName = dropTableName.substring(0, aliasIdx);
216       }
217
218       DatabaseTable table = dbs.getTable(dropTableName);
219       if (table == null)
220         throw new SQLException JavaDoc("Unknown table '" + dropTableName
221             + "' in FROM clause of this DELETE statement: '"
222             + sqlQueryOrTemplate + "'");
223       result.add(new AliasedDatabaseTable(table, alias));
224     }
225
226     return result;
227   }
228
229   /**
230    * Gets all the columns involved in the given <code>WHERE</code> clause.
231    * <p>
232    * The selected columns or tables must be found in the given
233    * <code>ArrayList</code> of <code>AliasedDatabaseTable</code>
234    * representing the <code>FROM</code> clause of the same request.
235    *
236    * @param whereClause <code>WHERE</code> clause of the request (without the
237    * <code>WHERE</code> keyword)
238    * @param aliasedFrom an <code>ArrayList</code> of
239    * <code>AliasedDatabaseTable</code>
240    * @return an <code>ArrayList</code> of <code>TableColumn</code>
241    */

242   private ArrayList JavaDoc getWhereColumns(String JavaDoc whereClause, ArrayList JavaDoc aliasedFrom)
243   {
244     ArrayList JavaDoc result = new ArrayList JavaDoc(); // TableColumn objects
245
ArrayList JavaDoc dbColumns = new ArrayList JavaDoc(); // DatabaseColumn objects
246

247     // Instead of parsing the clause, we use a brutal force technique
248
// and we try to directly identify every column name of each table.
249
DatabaseColumn col;
250     for (int i = 0; i < aliasedFrom.size(); i++)
251     {
252       DatabaseTable t = ((AliasedDatabaseTable) aliasedFrom.get(i)).getTable();
253       ArrayList JavaDoc cols = t.getColumns();
254       int size = cols.size();
255       for (int j = 0; j < size; j++)
256       {
257         col = (DatabaseColumn) cols.get(j);
258         // if pattern found and column not already in result, it's a dependency
259
// !
260
int matchIdx = whereClause.indexOf(col.getName());
261         while (matchIdx > 0)
262         {
263           // Try to check that we got the full pattern and not a sub-pattern
264
char beforePattern = whereClause.charAt(matchIdx - 1);
265           // Everything should be lowercase here
266
if (((beforePattern >= 'a') && (beforePattern <= 'z')) // Everything
267
|| (beforePattern == '_'))
268             matchIdx = whereClause.indexOf(col.getName(), matchIdx + 1);
269           else
270             break;
271         }
272         if (matchIdx == -1)
273           continue;
274         result.add(new TableColumn(t.getName(), col.getName()));
275         if (col.isUnique())
276           pkValue = col.getName();
277         dbColumns.add(col);
278       }
279     }
280
281     return result;
282   }
283
284   /**
285    * Returns an <code>ArrayList</code> of <code>String</code> objects
286    * representing the values associated with the unique columns involved in this
287    * request.
288    *
289    * @return an <code>ArrayList</code> value
290    */

291   public ArrayList JavaDoc getValues()
292   {
293     return whereValues;
294   }
295
296   /**
297    * Returns <code>true</code> if this query only deletes a single row.
298    *
299    * @return a <code>boolean</code> value
300    */

301   public boolean isUnique()
302   {
303     return isUnique;
304   }
305
306   /**
307    * @see org.continuent.sequoia.controller.requests.AbstractRequest#needsMacroProcessing()
308    */

309   public boolean needsMacroProcessing()
310   {
311     return true;
312   }
313
314   /**
315    * Parses the SQL request and extracts the selected columns and tables given
316    * the <code>DatabaseSchema</code> of the database targeted by this request.
317    * <p>
318    * An exception is thrown when the parsing fails. Warning, this method does
319    * not check the validity of the request. In particular, invalid request could
320    * be parsed without throwing an exception. However, valid SQL request should
321    * never throw an exception.
322    *
323    * @param schema a <code>DatabaseSchema</code> value
324    * @param granularity parsing granularity as defined in
325    * <code>ParsingGranularities</code>
326    * @param isCaseSensitive if parsing must be case sensitive
327    * @exception SQLException if the parsing fails
328    */

329   public void parse(DatabaseSchema schema, int granularity,
330       boolean isCaseSensitive) throws SQLException JavaDoc
331   {
332     if (granularity == ParsingGranularities.NO_PARSING)
333     {
334       isParsed = true;
335       return;
336     }
337
338     try
339     {
340       String JavaDoc originalSQL = this.trimCarriageReturnAndTabs();
341       String JavaDoc sql = originalSQL.toLowerCase();
342
343       int fromIdx = sql.indexOf("from ");
344       if (fromIdx == -1)
345       {
346         // For queries like: DELETE t WHERE ... used by Oracle
347
fromIdx = 6; // 6 = "delete".length()
348
}
349       else
350       {
351         // Syntax is usually DELETE FROM t WHERE ... but it can be
352
// DELETE t1 FROM t1,t2,.... WHERE ...
353
// If there is something between DELETE and FROM, tableName will use
354
// this
355
// name but the FROM clause will have all tables.
356
String JavaDoc tableBetweenDeleteAndFrom;
357         if (isCaseSensitive)
358           tableBetweenDeleteAndFrom = originalSQL.substring(6, fromIdx).trim();
359         else
360           tableBetweenDeleteAndFrom = sql.substring(6, fromIdx).trim();
361         if (tableBetweenDeleteAndFrom.length() == 0)
362           tableName = null;
363         else
364           tableName = tableBetweenDeleteAndFrom;
365         fromIdx += 5; // 5 = "from".length()
366
}
367
368       sql = sql.substring(fromIdx).trim();
369
370       // Look for the WHERE clause
371
int whereIdx = sql.indexOf("where ");
372
373       if (isCaseSensitive)
374         sql = originalSQL.substring(originalSQL.length() - sql.length());
375       if (tableName == null)
376       { // It was not a DELETE t1 FROM xxx type of query
377
if (whereIdx == -1)
378           tableName = sql;
379         else
380           tableName = sql.substring(0, whereIdx).trim();
381       }
382
383       if (schema == null)
384       {
385         writeLockedTables = new TreeSet JavaDoc();
386         writeLockedTables.add(tableName);
387         isParsed = true;
388         return;
389       }
390
391       // Get the table on which DELETE occurs
392
DatabaseTable t = schema.getTable(tableName, isCaseSensitive);
393       if (t == null)
394         throw new SQLException JavaDoc("Unknown table '" + tableName
395             + "' in this DELETE statement: " + sqlQueryOrTemplate + "'");
396       else
397         // Get the real name here (resolves case sentivity problems)
398
tableName = t.getName();
399
400       writeLockedTables = new TreeSet JavaDoc();
401       writeLockedTables.add(tableName);
402       addDependingTables(schema, writeLockedTables);
403
404       try
405       {
406         switch (granularity)
407         {
408           case ParsingGranularities.NO_PARSING :
409             return;
410           case ParsingGranularities.TABLE :
411             break;
412           case ParsingGranularities.COLUMN :
413             from = getFromTables(tableName, schema);
414             columns = getWhereColumns(sql.substring(whereIdx + 6).trim(), from);
415
416             if (from != null)
417             {
418               // Convert 'from' to an ArrayList of String objects instead of
419
// AliasedTables objects
420
int size = from.size();
421               ArrayList JavaDoc unaliased = new ArrayList JavaDoc(size);
422               for (int i = 0; i < size; i++)
423                 unaliased.add(((AliasedDatabaseTable) from.get(i)).getTable()
424                     .getName());
425               from = unaliased;
426             }
427             break;
428           case ParsingGranularities.COLUMN_UNIQUE :
429             from = getFromTables(tableName, schema);
430             columns = getWhereColumns(sql.substring(whereIdx + 6).trim(), from);
431
432             if (from != null)
433             {
434               // Convert 'from' to an ArrayList of String objects instead of
435
// AliasedTables objects
436
int size = from.size();
437               ArrayList JavaDoc unaliased = new ArrayList JavaDoc(size);
438               for (int i = 0; i < size; i++)
439                 unaliased.add(((AliasedDatabaseTable) from.get(i)).getTable()
440                     .getName());
441               from = unaliased;
442             }
443             break;
444           default :
445             throw new SQLException JavaDoc("Unsupported parsing granularity: '"
446                 + granularity + "'");
447         }
448       }
449       catch (SQLException JavaDoc e)
450       {
451         from = null;
452         columns = null;
453         whereValues = null;
454         throw e;
455       }
456
457       isParsed = true;
458     }
459     finally
460     {
461       if (isParsed)
462       {
463         SortedSet JavaDoc readSet = null;
464         if (from != null)
465           readSet = new TreeSet JavaDoc(from);
466         setSemantic(new SemanticBehavior(readSet, writeLockedTables, null,
467             altersDatabaseSchema(), altersMetadataCache(),
468             altersQueryResultCache(), altersUsers(), isReadOnly,
469             needsMacroProcessing(), SemanticBehavior.SERIALIZABLE_ORDER,
470             requiresConnectionPoolFlush
471                 ? SemanticBehavior.FLUSH_ALL_USERS
472                 : SemanticBehavior.FLUSH_NONE));
473       }
474     }
475   }
476
477   /**
478    * Does this request returns a ResultSet?
479    *
480    * @return false
481    */

482   public boolean returnsResultSet()
483   {
484     return false;
485   }
486
487   /**
488    * Displays some debugging information about this request.
489    */

490   public void debug()
491   {
492     super.debug();
493     System.out.println("Is unique: " + isUnique);
494     if (tableName != null)
495       System.out.println("Deleted table: " + tableName);
496     else
497       System.out.println("No information about deleted table");
498
499     if (columns != null)
500     {
501       System.out.println("Columns columns:");
502       for (int i = 0; i < columns.size(); i++)
503         System.out.println(" "
504             + ((TableColumn) columns.get(i)).getColumnName());
505     }
506     else
507       System.out.println("No information about updated columns");
508
509     System.out.println();
510   }
511
512   /**
513    * @see org.continuent.sequoia.controller.requests.AbstractRequest#getParsingResultsAsString()
514    */

515   public String JavaDoc getParsingResultsAsString()
516   {
517     StringBuffer JavaDoc sb = new StringBuffer JavaDoc(super.getParsingResultsAsString());
518     sb.append(Translate.get("request.delete.single.row", isUnique));
519     if (from != null && from.size() > 0)
520     {
521       sb.append(Translate.get("request.from.tables"));
522       for (int i = 0; i < from.size(); i++)
523       {
524         sb.append(Translate.get("request.from.table", from.get(i)));
525       }
526     }
527     if (whereValues != null && whereValues.size() > 0)
528     {
529       sb.append(Translate.get("request.where.tables"));
530       for (int i = 0; i < whereValues.size(); i++)
531       {
532         sb.append(Translate.get("request.where.table", whereValues.get(i)));
533       }
534     }
535     return sb.toString();
536   }
537 }
Popular Tags