KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > cjdbc > common > sql > DeleteRequest


1 /**
2  * C-JDBC: Clustered JDBC.
3  * Copyright (C) 2002-2005 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Contact: c-jdbc@objectweb.org
6  *
7  * This library is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by the
9  * Free Software Foundation; either version 2.1 of the License, or any later
10  * version.
11  *
12  * This library is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20  *
21  * Initial developer(s): Emmanuel Cecchet.
22  * Contributor(s): Julie Marguerite, Mathieu Peltier, Sara Bouchenak.
23  */

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

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

79   protected ArrayList JavaDoc whereValues;
80
81   /**
82    * Creates a new <code>DeleteRequest</code> instance. The caller must give
83    * an SQL request, without any leading or trailing spaces and beginning with
84    * 'delete ' (it will not be checked).
85    * <p>
86    * If the syntax is incorrect an exception is thrown.
87    *
88    * @param sqlQuery the SQL request
89    * @param escapeProcessing should the driver to escape processing before
90    * sending to the database ?
91    * @param timeout an <code>int</code> value
92    * @param lineSeparator the line separator used in the query
93    * @param schema a <code>DatabaseSchema</code> value
94    * @param granularity parsing granularity as defined in
95    * <code>ParsingGranularities</code>
96    * @param isCaseSensitive true if parsing is case sensitive
97    * @exception SQLException if an error occurs
98    */

99   public DeleteRequest(String JavaDoc sqlQuery, boolean escapeProcessing, int timeout,
100       String JavaDoc lineSeparator, DatabaseSchema schema, int granularity,
101       boolean isCaseSensitive) throws SQLException JavaDoc
102   {
103     this(sqlQuery, escapeProcessing, timeout, lineSeparator);
104     parse(schema, granularity, isCaseSensitive);
105   }
106
107   /**
108    * Creates a new <code>DeleteRequest</code> instance. The caller must give
109    * an SQL request, without any leading or trailing spaces and beginning with
110    * 'delete ' (it will not be checked).
111    * <p>
112    * The request is not parsed but it can be done later by a call to
113    * {@link #parse(DatabaseSchema, int, boolean)}.
114    *
115    * @param sqlQuery the SQL request
116    * @param escapeProcessing should the driver to escape processing before
117    * sending to the database ?
118    * @param timeout an <code>int</code> value
119    * @param lineSeparator the line separator used in the query
120    * @see #parse
121    */

122   public DeleteRequest(String JavaDoc sqlQuery, boolean escapeProcessing, int timeout,
123       String JavaDoc lineSeparator)
124   {
125     super(sqlQuery, escapeProcessing, timeout, lineSeparator,
126         RequestType.DELETE);
127   }
128
129   /**
130    * @see AbstractWriteRequest
131    */

132   public DeleteRequest(CJDBCInputStream in) throws IOException JavaDoc
133   {
134     super(in, RequestType.DELETE);
135   }
136
137   /**
138    * Parses the SQL request and extracts the selected columns and tables given
139    * the <code>DatabaseSchema</code> of the database targeted by this request.
140    * <p>
141    * An exception is thrown when the parsing fails. Warning, this method does
142    * not check the validity of the request. In particular, invalid request could
143    * be parsed without throwing an exception. However, valid SQL request should
144    * never throw an exception.
145    *
146    * @param schema a <code>DatabaseSchema</code> value
147    * @param granularity parsing granularity as defined in
148    * <code>ParsingGranularities</code>
149    * @param isCaseSensitive if parsing must be case sensitive
150    * @exception SQLException if the parsing fails
151    */

152   public void parse(DatabaseSchema schema, int granularity,
153       boolean isCaseSensitive) throws SQLException JavaDoc
154   {
155     if (granularity == ParsingGranularities.NO_PARSING)
156     {
157       isParsed = true;
158       return;
159     }
160
161     // Sanity check
162
if (schema == null)
163       throw new SQLException JavaDoc(
164           "Unable to parse request with an undefined database schema");
165
166     String JavaDoc originalSQL = this.trimCarriageReturnAndTabs();
167     String JavaDoc sql = originalSQL.toLowerCase();
168
169     int fromIdx = sql.indexOf("from ");
170     if (fromIdx == -1)
171     {
172       // For queries like: DELETE t WHERE ... used by Oracle
173
fromIdx = 6; // 6 = "delete".length()
174
}
175     else
176     {
177       // Syntax is usually DELETE FROM t WHERE ... but it can be
178
// DELETE t1 FROM t1,t2,.... WHERE ...
179
// If there is something between DELETE and FROM, tableName will use this
180
// name but the FROM clause will have all tables.
181
String JavaDoc tableBetweenDeleteAndFrom;
182       if (isCaseSensitive)
183         tableBetweenDeleteAndFrom = originalSQL.substring(6, fromIdx).trim();
184       else
185         tableBetweenDeleteAndFrom = sql.substring(6, fromIdx).trim();
186       if (tableBetweenDeleteAndFrom.length() == 0)
187         tableName = null;
188       else
189         tableName = tableBetweenDeleteAndFrom;
190       fromIdx += 5; // 5 = "from".length()
191
}
192
193     sql = sql.substring(fromIdx).trim();
194
195     // Look for the WHERE clause
196
int whereIdx = sql.indexOf("where ");
197
198     if (isCaseSensitive)
199       sql = originalSQL.substring(originalSQL.length() - sql.length());
200     if (tableName == null)
201     { // It was not a DELETE t1 FROM xxx type of query
202
if (whereIdx == -1)
203         tableName = sql;
204       else
205         tableName = sql.substring(0, whereIdx).trim();
206     }
207
208     // Get the table on which DELETE occurs
209
DatabaseTable t = schema.getTable(tableName, isCaseSensitive);
210     if (t == null)
211       throw new SQLException JavaDoc("Unknown table '" + tableName
212           + "' in this DELETE statement: " + sqlQuery + "'");
213     else
214       // Get the real name here (resolves case sentivity problems)
215
tableName = t.getName();
216
217     try
218     {
219       switch (granularity)
220       {
221         case ParsingGranularities.NO_PARSING :
222           return;
223         case ParsingGranularities.TABLE :
224           break;
225         case ParsingGranularities.COLUMN :
226           from = getFromTables(tableName, schema);
227           columns = getWhereColumns(sql.substring(whereIdx + 6).trim(), from);
228
229           if (from != null)
230           {
231             // Convert 'from' to an ArrayList of String objects instead of
232
// AliasedTables objects
233
int size = from.size();
234             ArrayList JavaDoc unaliased = new ArrayList JavaDoc(size);
235             for (int i = 0; i < size; i++)
236               unaliased.add(((AliasedDatabaseTable) from.get(i)).getTable()
237                   .getName());
238             from = unaliased;
239           }
240           break;
241         case ParsingGranularities.COLUMN_UNIQUE :
242           from = getFromTables(tableName, schema);
243           columns = getWhereColumns(sql.substring(whereIdx + 6).trim(), from);
244
245           if (from != null)
246           {
247             // Convert 'from' to an ArrayList of String objects instead of
248
// AliasedTables objects
249
int size = from.size();
250             ArrayList JavaDoc unaliased = new ArrayList JavaDoc(size);
251             for (int i = 0; i < size; i++)
252               unaliased.add(((AliasedDatabaseTable) from.get(i)).getTable()
253                   .getName());
254             from = unaliased;
255           }
256           break;
257         default :
258           throw new SQLException JavaDoc("Unsupported parsing granularity: '"
259               + granularity + "'");
260       }
261     }
262     catch (SQLException JavaDoc e)
263     {
264       from = null;
265       columns = null;
266       whereValues = null;
267       throw e;
268     }
269
270     isParsed = true;
271   }
272
273   /**
274    * @see AbstractRequest#cloneParsing(AbstractRequest)
275    */

276   public void cloneParsing(AbstractRequest request)
277   {
278     if (!request.isParsed())
279       return;
280     cloneTableNameAndColumns((AbstractWriteRequest) request);
281     isParsed = true;
282   }
283
284   /**
285    * Extracts the tables from the given <code>FROM</code> clause and retrieves
286    * their alias if any.
287    *
288    * @param fromClause the <code>FROM</code> clause of the request (without
289    * the <code>FROM</code> keyword)
290    * @param dbs the <code>DatabaseSchema</code> this request refers to
291    * @return an <code>ArrayList</code> of <code>AliasedDatabaseTable</code>
292    * objects
293    * @exception an <code>SQLException</code> if an error occurs
294    */

295   private ArrayList JavaDoc getFromTables(String JavaDoc fromClause, DatabaseSchema dbs)
296       throws SQLException JavaDoc
297   {
298     StringTokenizer JavaDoc tables = new StringTokenizer JavaDoc(fromClause, ",");
299     ArrayList JavaDoc result = new ArrayList JavaDoc(tables.countTokens());
300     while (tables.hasMoreTokens())
301     {
302       String JavaDoc tableName = tables.nextToken().trim();
303       // Check if the table has an alias
304
// Example: SELECT x.price FROM item x
305
String JavaDoc alias = null;
306       int aliasIdx = tableName.indexOf(' ');
307       if (aliasIdx != -1)
308       {
309         alias = tableName.substring(aliasIdx);
310         tableName = tableName.substring(0, aliasIdx);
311       }
312
313       DatabaseTable table = dbs.getTable(tableName);
314       if (table == null)
315         throw new SQLException JavaDoc("Unknown table '" + tableName
316             + "' in FROM clause of this DELETE statement: '" + sqlQuery + "'");
317       result.add(new AliasedDatabaseTable(table, alias));
318     }
319
320     return result;
321   }
322
323   /**
324    * Gets all the columns involved in the given <code>WHERE</code> clause.
325    * <p>
326    * The selected columns or tables must be found in the given
327    * <code>ArrayList</code> of <code>AliasedDatabaseTable</code>
328    * representing the <code>FROM</code> clause of the same request.
329    *
330    * @param whereClause <code>WHERE</code> clause of the request (without the
331    * <code>WHERE</code> keyword)
332    * @param aliasedFrom an <code>ArrayList</code> of
333    * <code>AliasedDatabaseTable</code>
334    * @return an <code>ArrayList</code> of <code>TableColumn</code>
335    */

336   private ArrayList JavaDoc getWhereColumns(String JavaDoc whereClause, ArrayList JavaDoc aliasedFrom)
337   {
338     ArrayList JavaDoc result = new ArrayList JavaDoc(); // TableColumn objects
339
ArrayList JavaDoc dbColumns = new ArrayList JavaDoc(); // DatabaseColumn objects
340

341     // Instead of parsing the clause, we use a brutal force technique
342
// and we try to directly identify every column name of each table.
343
DatabaseColumn col;
344     for (int i = 0; i < aliasedFrom.size(); i++)
345     {
346       DatabaseTable t = ((AliasedDatabaseTable) aliasedFrom.get(i)).getTable();
347       ArrayList JavaDoc cols = t.getColumns();
348       int size = cols.size();
349       for (int j = 0; j < size; j++)
350       {
351         col = (DatabaseColumn) cols.get(j);
352         // if pattern found and column not already in result, it's a dependency
353
// !
354
int matchIdx = whereClause.indexOf(col.getName());
355         while (matchIdx > 0)
356         {
357           // Try to check that we got the full pattern and not a sub-pattern
358
char beforePattern = whereClause.charAt(matchIdx - 1);
359           // Everything should be lowercase here
360
if (((beforePattern >= 'a') && (beforePattern <= 'z')) // Everything
361
|| (beforePattern == '_'))
362             matchIdx = whereClause.indexOf(col.getName(), matchIdx + 1);
363           else
364             break;
365         }
366         if (matchIdx == -1)
367           continue;
368         result.add(new TableColumn(t.getName(), col.getName()));
369         if (col.isUnique())
370           pkValue = col.getName();
371         dbColumns.add(col);
372       }
373     }
374
375     return result;
376   }
377
378   /**
379    * Returns an <code>ArrayList</code> of <code>String</code> objects
380    * representing the values associated with the unique columns involved in this
381    * request.
382    *
383    * @return an <code>ArrayList</code> value
384    */

385   public ArrayList JavaDoc getValues()
386   {
387     return whereValues;
388   }
389
390   /**
391    * Returns <code>true</code> if this query only deletes a single row.
392    *
393    * @return a <code>boolean</code> value
394    */

395   public boolean isUnique()
396   {
397     return isUnique;
398   }
399
400   /**
401    * @see org.objectweb.cjdbc.common.sql.AbstractRequest#needsMacroProcessing()
402    */

403   public boolean needsMacroProcessing()
404   {
405     return true;
406   }
407
408   /**
409    * @see org.objectweb.cjdbc.common.sql.AbstractRequest#returnsResultSet()
410    */

411   public boolean returnsResultSet()
412   {
413     return false;
414   }
415
416   /**
417    * Displays some debugging information about this request.
418    */

419   public void debug()
420   {
421     super.debug();
422     System.out.println("Is unique: " + isUnique);
423     if (tableName != null)
424       System.out.println("Deleted table: " + tableName);
425     else
426       System.out.println("No information about deleted table");
427
428     if (columns != null)
429     {
430       System.out.println("Columns columns:");
431       for (int i = 0; i < columns.size(); i++)
432         System.out.println(" "
433             + ((TableColumn) columns.get(i)).getColumnName());
434     }
435     else
436       System.out.println("No information about updated columns");
437
438     System.out.println();
439   }
440 }
Popular Tags