KickJava   Java API By Example, From Geeks To Geeks.

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


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  * Contact: sequoia@continuent.org
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  * Initial developer(s): Julie Marguerite.
21  * Contributor(s): Mathieu Peltier, Emmanuel Cecchet.
22  */

23
24 package org.continuent.sequoia.controller.requests;
25
26 import java.io.Serializable JavaDoc;
27 import java.sql.SQLException JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.Collection JavaDoc;
30 import java.util.SortedSet JavaDoc;
31 import java.util.StringTokenizer JavaDoc;
32 import java.util.TreeSet JavaDoc;
33 import java.util.regex.Matcher JavaDoc;
34 import java.util.regex.Pattern JavaDoc;
35
36 import org.continuent.sequoia.common.i18n.Translate;
37 import org.continuent.sequoia.controller.semantic.SemanticBehavior;
38 import org.continuent.sequoia.controller.sql.schema.DatabaseColumn;
39 import org.continuent.sequoia.controller.sql.schema.DatabaseSchema;
40 import org.continuent.sequoia.controller.sql.schema.DatabaseTable;
41 import org.continuent.sequoia.controller.sql.schema.TableColumn;
42
43 /**
44  * A <code>CreateRequest</code> is a SQL request of the following syntax:
45  *
46  * <pre>
47  * CREATE [TEMPORARY] TABLE table-name [(column-name column-type [,column-name colum-type]* [,table-constraint-definition]*)]
48  * </pre>
49  *
50  * We now also support SELECT INTO statements.
51  *
52  * @author <a HREF="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
53  * @author <a HREF="mailto:Mathieu.Peltier@inrialpes.fr">Mathieu Peltier </a>
54  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
55  * @version 1.0
56  */

57 public class CreateRequest extends AbstractWriteRequest implements Serializable JavaDoc
58 {
59   private static final long serialVersionUID = -8810498358284503952L;
60
61   /** The table to create. */
62   protected transient DatabaseTable table = null;
63
64   /**
65    * List of tables used to fill the created table in case of create query
66    * containing a select.
67    */

68   protected transient Collection JavaDoc fromTables = null;
69
70   // Be conservative, if we cannot parse the query, assumes it invalidates
71
// everything
72
protected boolean altersDatabaseCatalog = true;
73   protected boolean altersDatabaseSchema = true;
74   protected boolean altersSomething = true;
75   protected boolean altersStoredProcedureList = true;
76   protected boolean altersUserDefinedTypes = true;
77   protected boolean altersUsers = true;
78
79   protected boolean altersAggregateList = false;
80   protected boolean altersMetadataCache = false;
81   protected boolean altersQueryResultCache = false;
82
83   /**
84    * Creates a new <code>CreateRequest</code> instance. The caller must give
85    * an SQL request, without any leading or trailing spaces and beginning with
86    * 'create table ' (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 CreateRequest(String JavaDoc sqlQuery, boolean escapeProcessing, int timeout,
99       String JavaDoc lineSeparator)
100   {
101     super(sqlQuery, escapeProcessing, timeout, lineSeparator,
102         RequestType.CREATE);
103     setMacrosAreProcessed(true); // no macro processing needed
104
}
105
106   /**
107    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersAggregateList()
108    */

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

117   public boolean altersDatabaseCatalog()
118   {
119     return altersDatabaseCatalog;
120   }
121
122   /**
123    * Returns true if this create request alters the current database schema
124    * (using create table, create schema, create view) and false otherwise
125    * (create database, create index, create function, create method, create
126    * procedure, create role, create trigger, create type).
127    *
128    * @return Returns true if this query alters the database schema.
129    */

130   public boolean altersDatabaseSchema()
131   {
132     return altersDatabaseSchema;
133   }
134
135   /**
136    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersMetadataCache()
137    */

138   public boolean altersMetadataCache()
139   {
140     return altersMetadataCache;
141   }
142
143   /**
144    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersQueryResultCache()
145    */

146   public boolean altersQueryResultCache()
147   {
148     return altersQueryResultCache;
149   }
150
151   /**
152    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersSomething()
153    */

154   public boolean altersSomething()
155   {
156     return altersSomething;
157   }
158
159   /**
160    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersStoredProcedureList()
161    */

162   public boolean altersStoredProcedureList()
163   {
164     return altersStoredProcedureList;
165   }
166
167   /**
168    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersUserDefinedTypes()
169    */

170   public boolean altersUserDefinedTypes()
171   {
172     return altersUserDefinedTypes;
173   }
174
175   /**
176    * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersUsers()
177    */

178   public boolean altersUsers()
179   {
180     return altersUsers;
181   }
182
183   /**
184    * @see AbstractRequest#cloneParsing(AbstractRequest)
185    */

186   public void cloneParsing(AbstractRequest request)
187   {
188     if (!request.isParsed())
189       return;
190     cloneParsingCommons(request);
191     CreateRequest createRequest = (CreateRequest) request;
192     cloneTableNameAndColumns(createRequest);
193     table = createRequest.getDatabaseTable();
194     fromTables = createRequest.getFromTables();
195     isParsed = true;
196   }
197
198   /**
199    * Gets the database table created by this statement (in case of a CREATE
200    * TABLE statement).
201    *
202    * @return a <code>DatabaseTable</code> value
203    */

204   public DatabaseTable getDatabaseTable()
205   {
206     return table;
207   }
208
209   /**
210    * Returns the list of tables used to fill the created table in case of create
211    * query containing a select.
212    *
213    * @return <code>Collection</code> of tables
214    */

215   public Collection JavaDoc getFromTables()
216   {
217     return fromTables;
218   }
219
220   /**
221    * @see org.continuent.sequoia.controller.requests.AbstractRequest#needsMacroProcessing()
222    */

223   public boolean needsMacroProcessing()
224   {
225     return false;
226   }
227
228   /**
229    * Pattern for SELECT INTO statement. This permits to extract from the
230    * statement the name of the table.
231    */

232   private static final String JavaDoc CREATE_TABLE_SELECT_INTO = "^select.*into\\s+((temporary|temp)\\s+)?(table\\s+)?([^(\\s|\\()]+)(.*)?";
233   private static final Pattern JavaDoc CREATE_TABLE_SELECT_INTO_STATEMENT_PATTERN = Pattern
234                                                                               .compile(
235                                                                                   CREATE_TABLE_SELECT_INTO,
236                                                                                   Pattern.CASE_INSENSITIVE
237                                                                                       | Pattern.DOTALL);
238
239   /**
240    * @see org.continuent.sequoia.controller.requests.AbstractRequest#parse(org.continuent.sequoia.controller.sql.schema.DatabaseSchema,
241    * int, boolean)
242    */

243   public void parse(DatabaseSchema schema, int granularity,
244       boolean isCaseSensitive) throws SQLException JavaDoc
245   {
246     if (granularity == ParsingGranularities.NO_PARSING)
247     {
248       isParsed = true;
249       return;
250     }
251
252     try
253     {
254       String JavaDoc originalSQL = this.trimCarriageReturnAndTabs();
255       String JavaDoc sql = originalSQL.toLowerCase();
256
257       Matcher JavaDoc matcher = CREATE_TABLE_SELECT_INTO_STATEMENT_PATTERN.matcher(sql);
258       if (matcher.matches())
259       {
260         // INSERT... INTO
261
table = new DatabaseTable(matcher.group(4));
262
263         altersDatabaseCatalog = false;
264         altersDatabaseSchema = true;
265         altersStoredProcedureList = false;
266         altersUserDefinedTypes = false;
267         altersUsers = false;
268         isParsed = true;
269         return;
270       }
271
272       // Strip create
273
sql = sql.substring("create".length()).trim();
274
275       // Check what kind of create we are facing
276
if (sql.startsWith("database"))
277       { // CREATE DATABASE alters only the database catalog
278
altersDatabaseCatalog = true;
279         altersDatabaseSchema = false;
280         altersStoredProcedureList = false;
281         altersUserDefinedTypes = false;
282         altersUsers = false;
283         return;
284       }
285       if (sql.startsWith("index") || sql.startsWith("unique")
286           || sql.startsWith("role") || sql.startsWith("sequence"))
287       { // Does not alter anything :
288
// CREATE [UNIQUE] INDEX
289
// CREATE ROLE
290
// CREATE SEQUENCE
291
altersSomething = false;
292         return;
293       }
294       if (sql.startsWith("function") || sql.startsWith("method")
295           || sql.startsWith("procedure") || sql.startsWith("trigger")
296           || sql.startsWith("type"))
297       { // CREATE FUNCTION, CREATE METHOD, CREATE PROCEDURE, CREATE TRIGGER and
298
// CREATE TYPE only alters definitions
299
altersDatabaseCatalog = false;
300         altersDatabaseSchema = false;
301         altersStoredProcedureList = true;
302         altersUserDefinedTypes = true;
303         altersUsers = false;
304         return;
305       }
306
307       // From this point on, only the schema is affected
308
altersDatabaseCatalog = false;
309       altersDatabaseSchema = true;
310       altersStoredProcedureList = false;
311       altersUserDefinedTypes = false;
312       altersUsers = false;
313       if (sql.startsWith("schema") || sql.startsWith("view"))
314         // No parsing to do
315
return;
316
317       // Let's try to check if we have a 'create [temporary] table '
318
int tableIdx = sql.indexOf("table");
319       if (tableIdx < 0)
320         throw new SQLException JavaDoc("Unsupported CREATE statement: '"
321             + sqlQueryOrTemplate + "'");
322
323       //
324
// Starting from here, everything is just to handle CREATE TABLE
325
// statements
326
//
327

328       // Strip up to 'table'
329
sql = sql.substring(tableIdx + 5).trim();
330
331       // Does the query contain a select?
332
int selectIdx = sql.indexOf("select");
333       if (selectIdx != -1 && sql.charAt(selectIdx + 6) != ' ')
334         selectIdx = -1;
335
336       if (isCaseSensitive) // Reverse to the original case
337
sql = originalSQL.substring(originalSQL.length() - sql.length());
338
339       if (selectIdx != -1)
340       {
341         // Get the table on which CREATE occurs
342
int nextSpaceIdx = sql.indexOf(" ");
343         tableName = sql.substring(0, nextSpaceIdx).trim();
344         table = new DatabaseTable(tableName);
345         // Parse the select
346
sql = sql.substring(selectIdx).trim();
347         SelectRequest select = new SelectRequest(sql, false, 60,
348             getLineSeparator());
349         select.parse(schema, granularity, isCaseSensitive);
350         fromTables = select.getFrom();
351         if (granularity > ParsingGranularities.TABLE)
352         { // Update the columns and add them to the table
353
columns = select.getSelect();
354           int size = columns.size();
355           for (int i = 0; i < size; i++)
356           {
357             TableColumn tc = (TableColumn) columns.get(i);
358             table.addColumn(new DatabaseColumn(tc.getColumnName(), false));
359           }
360         }
361       }
362       else
363       {
364         // Get the table on which CREATE occurs
365
// Look for the parenthesis
366
int openParenthesisIdx = sql.indexOf("(");
367         int closeParenthesisIdx = sql.lastIndexOf(")");
368         if ((openParenthesisIdx == -1) && (closeParenthesisIdx == -1))
369         {
370           // no parenthesis found
371
table = new DatabaseTable(sql.trim());
372           if (granularity > ParsingGranularities.TABLE)
373             columns = new ArrayList JavaDoc();
374           isParsed = true;
375           return;
376         }
377         else if ((openParenthesisIdx == -1) || (closeParenthesisIdx == -1)
378             || (openParenthesisIdx > closeParenthesisIdx))
379         {
380           throw new SQLException JavaDoc("Syntax error in this CREATE statement: '"
381               + sqlQueryOrTemplate + "'");
382         }
383         else
384         {
385           tableName = sql.substring(0, openParenthesisIdx).trim();
386         }
387         table = new DatabaseTable(tableName);
388
389         // Get the column names
390
if (granularity > ParsingGranularities.TABLE)
391         {
392           columns = new ArrayList JavaDoc();
393           sql = sql.substring(openParenthesisIdx + 1, closeParenthesisIdx)
394               .trim();
395           StringTokenizer JavaDoc columnTokens = new StringTokenizer JavaDoc(sql, ",");
396           String JavaDoc word;
397           String JavaDoc lowercaseWord;
398           StringTokenizer JavaDoc wordTokens = null;
399           String JavaDoc token;
400           DatabaseColumn col = null;
401
402           while (columnTokens.hasMoreTokens())
403           {
404             token = columnTokens.nextToken().trim();
405
406             // work around to prevent bug: if the request contains for example:
407
// INDEX foo (col1,col2)
408
// we have to merge the 2 tokens: 'INDEX foo (col1' and 'col2)'
409
if ((token.indexOf("(") != -1) && (token.indexOf(")") == -1))
410             {
411               if (columnTokens.hasMoreTokens())
412                 token = token + "," + columnTokens.nextToken().trim();
413               else
414               {
415                 tableName = null;
416                 columns = null;
417                 throw new SQLException JavaDoc(
418                     "Syntax error in this CREATE statement: '"
419                         + sqlQueryOrTemplate + "'");
420               }
421             }
422
423             // First word of the line: either a column name or
424
// a table constraint definition
425
wordTokens = new StringTokenizer JavaDoc(token, " ");
426             word = wordTokens.nextToken().trim();
427             lowercaseWord = word.toLowerCase();
428
429             // If it's a constraint, index or check keyword do not do anything
430
// else parse the line
431
if (!lowercaseWord.equals("constraint")
432                 && !lowercaseWord.equals("index")
433                 && !lowercaseWord.equals("check"))
434             {
435               String JavaDoc columnName;
436               boolean isUnique = false;
437               // Check for primary key or unique constraint
438
if (lowercaseWord.equals("primary")
439                   || lowercaseWord.startsWith("unique"))
440               {
441
442                 // Get the name of the column
443
openParenthesisIdx = token.indexOf("(");
444                 closeParenthesisIdx = token.indexOf(")");
445                 if ((openParenthesisIdx == -1) || (closeParenthesisIdx == -1)
446                     || (openParenthesisIdx > closeParenthesisIdx))
447                 {
448                   tableName = null;
449                   columns = null;
450                   throw new SQLException JavaDoc(
451                       "Syntax error in this CREATE statement: '"
452                           + sqlQueryOrTemplate + "'");
453                 }
454
455                 columnName = token.substring(openParenthesisIdx + 1,
456                     closeParenthesisIdx).trim();
457
458                 int comma;
459                 while ((comma = columnName.indexOf(',')) != -1)
460                 {
461                   String JavaDoc col1 = columnName.substring(0, comma).trim();
462                   col = table.getColumn(col1);
463                   if (col == null)
464                   {
465                     tableName = null;
466                     columns = null;
467                     throw new SQLException JavaDoc(
468                         "Syntax error in this CREATE statement: '"
469                             + sqlQueryOrTemplate + "'");
470                   }
471                   else
472                     col.setIsUnique(true);
473                   columnName = columnName.substring(comma + 1);
474                 }
475
476                 // Set this column to unique
477
col = table.getColumn(columnName);
478
479                 // Test first if dbTable contains this column. This can fail
480
// with
481
// some invalid request, for example:
482
// CREATE TABLE categories(id INT4, name TEXT, PRIMARY KEY((id))
483
if (col == null)
484                 {
485                   tableName = null;
486                   columns = null;
487                   throw new SQLException JavaDoc(
488                       "Syntax error in this CREATE statement: '"
489                           + sqlQueryOrTemplate + "'");
490                 }
491                 else
492                   col.setIsUnique(true);
493               }
494               else
495               {
496                 // It's a column name
497
columnName = word;
498
499                 if (!wordTokens.hasMoreTokens())
500                 {
501                   // at least type declaration is required
502
tableName = null;
503                   columns = null;
504                   throw new SQLException JavaDoc(
505                       "Syntax error in this CREATE statement: '"
506                           + sqlQueryOrTemplate + "'");
507                 }
508
509                 // Check for primary key or unique constraints
510
do
511                 {
512                   word = wordTokens.nextToken().trim().toLowerCase();
513                   if (word.equals("primary") || word.startsWith("unique"))
514                   {
515                     // Create the column as unique
516
isUnique = true;
517                     break;
518                   }
519                 }
520                 while (wordTokens.hasMoreTokens());
521
522                 // Add the column to the parsed columns list and
523
// to the create DatabaseTable
524
columns.add(new TableColumn(tableName, columnName));
525                 table.addColumn(new DatabaseColumn(columnName, isUnique));
526               }
527             }
528           }
529         }
530       }
531       isParsed = true;
532     }
533     finally
534     {
535       if (isParsed)
536       {
537         SortedSet JavaDoc readSet = null;
538         if (fromTables != null)
539           readSet = new TreeSet JavaDoc(fromTables);
540         setSemantic(new SemanticBehavior(readSet, writeLockedTables, null,
541             altersDatabaseSchema, altersMetadataCache, altersQueryResultCache,
542             altersUsers, isReadOnly, needsMacroProcessing(),
543             SemanticBehavior.SERIALIZABLE_ORDER, requiresConnectionPoolFlush
544                 ? SemanticBehavior.FLUSH_ALL_USERS
545                 : SemanticBehavior.FLUSH_NONE));
546       }
547     }
548   }
549
550   /**
551    * Does this request returns a ResultSet?
552    *
553    * @return false
554    */

555   public boolean returnsResultSet()
556   {
557     return false;
558   }
559
560   /**
561    * @see org.continuent.sequoia.controller.requests.AbstractRequest#getParsingResultsAsString()
562    */

563   public String JavaDoc getParsingResultsAsString()
564   {
565     StringBuffer JavaDoc sb = new StringBuffer JavaDoc(super.getParsingResultsAsString());
566     sb.append(Translate.get("request.create.table", table));
567     if (fromTables != null)
568     {
569       sb.append(Translate.get("request.create.from.tables"));
570       for (int i = 0; i < fromTables.size(); i++)
571         sb.append(Translate.get("request.create.from.table", fromTables
572             .toArray()[i]));
573     }
574     sb.append(Translate.get("request.alters",
575         new String JavaDoc[]{String.valueOf(altersAggregateList()),
576             String.valueOf(altersDatabaseCatalog()),
577             String.valueOf(altersDatabaseSchema()),
578             String.valueOf(altersMetadataCache()),
579             String.valueOf(altersQueryResultCache()),
580             String.valueOf(altersSomething()),
581             String.valueOf(altersStoredProcedureList()),
582             String.valueOf(altersUserDefinedTypes()),
583             String.valueOf(altersUsers())}));
584     return sb.toString();
585   }
586
587   /**
588    * Displays some debugging information about this request.
589    */

590   public void debug()
591   {
592     super.debug();
593     if (tableName != null)
594       System.out.println("Created table: " + tableName);
595     else
596       System.out.println("No information about created table");
597
598     if (columns != null)
599     {
600       System.out.println("Created columns:");
601       for (int i = 0; i < columns.size(); i++)
602         System.out.println(" "
603             + ((TableColumn) columns.get(i)).getColumnName());
604     }
605     else
606       System.out.println("No information about created columns");
607
608     System.out.println();
609   }
610
611 }
Popular Tags