00001 /** 00002 * C-JDBC: Clustered JDBC. 00003 * Copyright (C) 2002-2004 French National Institute For Research In Computer 00004 * Science And Control (INRIA). 00005 * Contact: c-jdbc@objectweb.org 00006 * 00007 * This library is free software; you can redistribute it and/or modify it 00008 * under the terms of the GNU Lesser General Public License as published by the 00009 * Free Software Foundation; either version 2.1 of the License, or any later 00010 * version. 00011 * 00012 * This library is distributed in the hope that it will be useful, but WITHOUT 00013 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00014 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 00015 * for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public License 00018 * along with this library; if not, write to the Free Software Foundation, 00019 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 00020 * 00021 * Initial developer(s): Emmanuel Cecchet. 00022 * Contributor(s): Julie Marguerite, Mathieu Peltier. 00023 */ 00024 00025 package org.objectweb.cjdbc.common.sql; 00026 00027 import java.io.Serializable; 00028 import java.sql.SQLException; 00029 00030 import org.objectweb.cjdbc.common.sql.schema.DatabaseSchema; 00031 00032 /** 00033 * An <code>AbstractRequest</code> defines the skeleton of an SQL request. 00034 * 00035 * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a> 00036 * @author <a href="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a> 00037 * @author <a href="mailto:Mathieu.Peltier@inrialpes.fr">Mathieu Peltier </a> 00038 * @version 1.0 00039 */ 00040 public abstract class AbstractRequest implements Serializable 00041 { 00042 /** Request unique id (set by the controller). */ 00043 protected transient long id; 00044 00045 /** SQL query (should be set in constructor). */ 00046 protected String sqlQuery; 00047 00048 /** SQL query skeleton as it appears in PreparedStatements. */ 00049 protected String sqlSkeleton = null; 00050 00051 /** 00052 * Login used to issue this request (must be set by the 00053 * VirtualDatabaseWorkerThread). 00054 */ 00055 protected String login; 00056 00057 /** Whether this request is cacheable or not. */ 00058 protected int cacheable; 00059 00060 /** Whether the SQL content has been parsed or not. */ 00061 protected boolean isParsed; 00062 00063 /** 00064 * Maximum number of rows in the ResultSet. 00065 * 00066 * @see java.sql.Statement#setMaxRows(int) 00067 */ 00068 protected int maxRows; 00069 00070 private int fetchSize = 0; 00071 00072 // 00073 // Connection related parameters 00074 // 00075 00076 /** True if the connection has been set to read-only */ 00077 protected boolean isReadOnly = false; 00078 00079 /** 00080 * Whether this request has been sent in <code>autocommit</code> mode or 00081 * not. 00082 */ 00083 protected boolean isAutoCommit; 00084 00085 /** 00086 * Transaction identifier if this request belongs to a transaction. The value 00087 * is set by the VirtualDatabaseWorkerThread. 00088 */ 00089 protected long transactionId; 00090 00091 /** 00092 * Timeout for this request in seconds, value 0 means no timeout (should be 00093 * set in constructor). 00094 */ 00095 protected int timeout; 00096 00097 /** 00098 * Should the driver do escape processing before sending to the database? No 00099 * setter for this member, should be set in constructor. 00100 */ 00101 protected boolean escapeProcessing = true; 00102 00103 /** 00104 * Should match System.getProperty("line.separator") on the driver side. 00105 * Required for parsing. 00106 */ 00107 private String lineSeparator = null; 00108 00109 /** 00110 * If set to true, the query is interpreted on the driver side, if false the 00111 * various parameters are encoded and passed as is to the database native 00112 * driver by the controller. 00113 */ 00114 private boolean driverProcessed = true; 00115 00116 /** 00117 * Default constructor Creates a new <code>AbstractRequest</code> object 00118 * 00119 * @param sqlQuery the SQL query 00120 * @param escapeProcessing should the driver to escape processing before 00121 * sending to the database ? 00122 * @param timeout an <code>int</code> value 00123 * @param lineSeparator the line separator used in the query 00124 */ 00125 public AbstractRequest(String sqlQuery, boolean escapeProcessing, 00126 int timeout, String lineSeparator) 00127 { 00128 this.sqlQuery = sqlQuery; 00129 this.escapeProcessing = escapeProcessing; 00130 this.timeout = timeout; 00131 this.lineSeparator = lineSeparator; 00132 } 00133 00134 /** 00135 * Returns <code>true</code> if this request is a read request ( 00136 * <code>SELECT</code> requests for example perform a read). 00137 * 00138 * @return <code>true</code> if this request is a read request 00139 */ 00140 public abstract boolean isReadRequest(); 00141 00142 /** 00143 * Returns <code>true</code> if this request is a write request ( 00144 * <code>INSERT</code> or <code>UPDATE</code> for example perform writes). 00145 * 00146 * @return <code>true</code> if this requests is a write request 00147 */ 00148 public abstract boolean isWriteRequest(); 00149 00150 /** 00151 * Returns <code>true</code> if the resulting operation on this request is 00152 * unknown (some non-standard command or stored procedure for example). 00153 * 00154 * @return a <code>boolean</code> value 00155 */ 00156 public abstract boolean isUnknownRequest(); 00157 00158 /** 00159 * Returns <code>true</code> if the request SQL content has been already 00160 * parsed. 00161 * 00162 * @return a <code>boolean</code> value 00163 */ 00164 public boolean isParsed() 00165 { 00166 return isParsed; 00167 } 00168 00169 /** 00170 * Returns <code>true</code> if the connection is set to read-only 00171 * 00172 * @return a <code>boolean</code> value 00173 */ 00174 public boolean isReadOnly() 00175 { 00176 return isReadOnly; 00177 } 00178 00179 /** 00180 * Sets the read-only mode for this request. 00181 * 00182 * @param isReadOnly <code>true</code> if connection is read-only 00183 */ 00184 public void setIsReadOnly(boolean isReadOnly) 00185 { 00186 this.isReadOnly = isReadOnly; 00187 } 00188 00189 /** 00190 * Returns the cacheable status of this request. It can be: 00191 * {@link org.objectweb.cjdbc.common.sql.RequestType#CACHEABLE}, 00192 * {@link org.objectweb.cjdbc.common.sql.RequestType#UNCACHEABLE}or 00193 * {@link org.objectweb.cjdbc.common.sql.RequestType#UNIQUE_CACHEABLE} 00194 * 00195 * @return a <code>int</code> value 00196 */ 00197 public int getCacheAbility() 00198 { 00199 return cacheable; 00200 } 00201 00202 /** 00203 * Set the cacheable status of this request. It can be: 00204 * {@link org.objectweb.cjdbc.common.sql.RequestType#CACHEABLE}, 00205 * {@link org.objectweb.cjdbc.common.sql.RequestType#UNCACHEABLE}or 00206 * {@link org.objectweb.cjdbc.common.sql.RequestType#UNIQUE_CACHEABLE} 00207 * 00208 * @param cacheAbility a <code>int</code> value 00209 */ 00210 public void setCacheAbility(int cacheAbility) 00211 { 00212 this.cacheable = cacheAbility; 00213 } 00214 00215 /** 00216 * Returns <code>true</code> if the driver should escape processing before 00217 * sending to the database? 00218 * 00219 * @return a <code>boolean</code> value 00220 */ 00221 public boolean getEscapeProcessing() 00222 { 00223 return escapeProcessing; 00224 } 00225 00226 /** 00227 * Returns the unique id of this request. 00228 * 00229 * @return the request id 00230 */ 00231 public long getId() 00232 { 00233 return id; 00234 } 00235 00236 /** 00237 * Sets the unique id of this request. 00238 * 00239 * @param id the id to set 00240 */ 00241 public void setId(long id) 00242 { 00243 this.id = id; 00244 } 00245 00246 /** 00247 * Returns <code>true</code> if the request should be executed in 00248 * <code>autocommit</code> mode. 00249 * 00250 * @return a <code>boolean</code> value 00251 */ 00252 public boolean isAutoCommit() 00253 { 00254 return isAutoCommit; 00255 } 00256 00257 /** 00258 * Sets the autocommit mode for this request. 00259 * 00260 * @param isAutoCommit <code>true</code> if <code>autocommit</code> should 00261 * be used 00262 */ 00263 public void setIsAutoCommit(boolean isAutoCommit) 00264 { 00265 this.isAutoCommit = isAutoCommit; 00266 } 00267 00268 /** 00269 * Returns the login used to issue this request. 00270 * 00271 * @return a <code>String</code> value 00272 */ 00273 public String getLogin() 00274 { 00275 return login; 00276 } 00277 00278 /** 00279 * Returns the lineSeparator value. 00280 * 00281 * @return Returns the lineSeparator. 00282 */ 00283 public String getLineSeparator() 00284 { 00285 return lineSeparator; 00286 } 00287 00288 /** 00289 * Sets the lineSeparator value. 00290 * 00291 * @param lineSeparator The lineSeparator to set. 00292 */ 00293 public void setLineSeparator(String lineSeparator) 00294 { 00295 this.lineSeparator = lineSeparator; 00296 } 00297 00298 /** 00299 * Sets the login to use to issue this request. 00300 * 00301 * @param login a <code>String</code> value 00302 */ 00303 public void setLogin(String login) 00304 { 00305 this.login = login; 00306 } 00307 00308 /** 00309 * Gets the SQL code of this request. 00310 * 00311 * @return the SQL query 00312 */ 00313 public String getSQL() 00314 { 00315 return sqlQuery; 00316 } 00317 00318 /** 00319 * Get a short form of this request if the SQL statement exceeds 00320 * nbOfCharacters. 00321 * 00322 * @param nbOfCharacters number of characters to include in the short form. 00323 * @return the nbOfCharacters first characters of the SQL statement 00324 */ 00325 public String getSQLShortForm(int nbOfCharacters) 00326 { 00327 if ((nbOfCharacters == 0) || (sqlQuery.length() < nbOfCharacters)) 00328 return sqlQuery; 00329 else 00330 return sqlQuery.substring(0, nbOfCharacters) + "..."; 00331 } 00332 00333 /** 00334 * Get the maximum number of rows the ResultSet can contain. 00335 * 00336 * @return maximum number of rows 00337 * @see java.sql.Statement#getMaxRows() 00338 */ 00339 public int getMaxRows() 00340 { 00341 return maxRows; 00342 } 00343 00344 /** 00345 * Set the maximum number of rows in the ResultSet. 00346 * 00347 * @param rows maximum number of rows 00348 * @see java.sql.Statement#setMaxRows(int) 00349 */ 00350 public void setMaxRows(int rows) 00351 { 00352 maxRows = rows; 00353 } 00354 00355 /** 00356 * Set the SQL code of this request. Warning! The request parsing validity is 00357 * not checked. The caller has to recall 00358 * {@link #parse(DatabaseSchema, int, boolean)}if needed. 00359 * 00360 * @param sql SQL statement 00361 */ 00362 public void setSQL(String sql) 00363 { 00364 this.sqlQuery = sql; 00365 } 00366 00367 /** 00368 * Gets the timeout for this request in seconds. 00369 * 00370 * @return timeout in seconds (0 means no timeout) 00371 */ 00372 public int getTimeout() 00373 { 00374 return timeout; 00375 } 00376 00377 /** 00378 * Sets the new timeout in seconds for this request. 00379 * 00380 * @param timeout an <code>int</code> value 00381 */ 00382 public void setTimeout(int timeout) 00383 { 00384 this.timeout = timeout; 00385 } 00386 00387 /** 00388 * Gets the identifier of the transaction if this request belongs to a 00389 * transaction, or -1 if this request does not belong to a transaction. 00390 * 00391 * @return transaction identifier or -1 00392 */ 00393 public long getTransactionId() 00394 { 00395 return transactionId; 00396 } 00397 00398 /** 00399 * Sets the transaction identifier this request belongs to. 00400 * 00401 * @param id transaction id 00402 */ 00403 public void setTransactionId(long id) 00404 { 00405 transactionId = id; 00406 } 00407 00408 /** 00409 * Two requests are equal if they have the same SQL code. 00410 * 00411 * @param other an object 00412 * @return a <code>boolean</code> value 00413 */ 00414 public boolean equals(Object other) 00415 { 00416 if (!(other instanceof AbstractRequest)) 00417 return false; 00418 00419 AbstractRequest r = (AbstractRequest) other; 00420 return sqlQuery.equals(r.getSQL()) 00421 && (transactionId == r.getTransactionId()); 00422 } 00423 00424 /** 00425 * Parses the SQL request and extract the selected columns and tables given 00426 * the <code>DatabaseSchema</code> of the database targeted by this request. 00427 * <p> 00428 * An exception is thrown when the parsing fails. Warning, this method does 00429 * not check the validity of the request. In particular, invalid request could 00430 * be parsed without throwing an exception. However, valid SQL request should 00431 * never throw an exception. 00432 * 00433 * @param schema a <code>DatabaseSchema</code> value 00434 * @param granularity parsing granularity as defined in 00435 * <code>ParsingGranularities</code> 00436 * @param isCaseSensitive true if parsing must be case sensitive 00437 * @exception SQLException if the parsing fails 00438 */ 00439 public abstract void parse(DatabaseSchema schema, int granularity, 00440 boolean isCaseSensitive) throws SQLException; 00441 00442 /** 00443 * Clones the parsing of a request. 00444 * 00445 * @param request the parsed request to clone 00446 */ 00447 public abstract void cloneParsing(AbstractRequest request); 00448 00449 /** 00450 * If the query has a skeleton defined, return the skeleton wth all carriage 00451 * returns replaces with spaces. If no SQL skeleton is defined, we perform the 00452 * same processing on the instanciated SQL statement. 00453 * 00454 * @return statement with CR replaces by spaces 00455 */ 00456 public String trimCarriageReturn() 00457 { 00458 if (sqlSkeleton != null) 00459 return trimCarriageReturn(sqlSkeleton); 00460 else 00461 return trimCarriageReturn(sqlQuery); 00462 } 00463 00464 /** 00465 * Replaces any carriage returns by a space in a given <code>String</code>. 00466 * 00467 * @param s the <code>String</code> to transform 00468 * @return the transformed <code>String</code> 00469 */ 00470 private String trimCarriageReturn(String s) 00471 { 00472 int lineSeparatorLength = lineSeparator.length(); 00473 int idx = s.indexOf(lineSeparator); 00474 if (idx == -1) 00475 return s; 00476 else 00477 { 00478 if (idx == 0) // carriage is the first character 00479 return trimCarriageReturn(s.substring(lineSeparatorLength)); 00480 else if (idx == (s.length() - lineSeparatorLength)) // is the last 00481 // character 00482 return s.substring(0, s.length() - lineSeparatorLength); 00483 else 00484 // is somewhere in the string 00485 return s.substring(0, idx) + " " 00486 + trimCarriageReturn(s.substring(idx + lineSeparatorLength)); 00487 } 00488 } 00489 00490 /** 00491 * @return the SQL query skeleton given in a <code>PreparedStatement</code>. 00492 */ 00493 public String getSqlSkeleton() 00494 { 00495 return sqlSkeleton; 00496 } 00497 00498 /** 00499 * @param skel set the SQL query skeleton given in a 00500 * <code>PreparedStatement</code>. 00501 */ 00502 public void setSqlSkeleton(String skel) 00503 { 00504 sqlSkeleton = skel; 00505 } 00506 00507 /** 00508 * Returns the driverProcessed value. 00509 * 00510 * @return Returns the driverProcessed. 00511 */ 00512 public boolean isDriverProcessed() 00513 { 00514 return driverProcessed; 00515 } 00516 00517 /** 00518 * Sets the driverProcessed value. 00519 * 00520 * @param driverProcessed The driverProcessed to set. 00521 */ 00522 public void setDriverProcessed(boolean driverProcessed) 00523 { 00524 this.driverProcessed = driverProcessed; 00525 } 00526 00527 /** 00528 * Sets the fetchSize value. 00529 * 00530 * @param fetchSize The fetchSize to set. 00531 */ 00532 public void setFetchSize(int fetchSize) 00533 { 00534 this.fetchSize = fetchSize; 00535 } 00536 00537 /** 00538 * Returns the fetchSize value. 00539 * 00540 * @return Returns the fetchSize. 00541 */ 00542 public int getFetchSize() 00543 { 00544 return fetchSize; 00545 } 00546 00547 /** 00548 * Displays some debugging information about this request. 00549 */ 00550 public void debug() 00551 { 00552 System.out.println("Request: " + sqlQuery); 00553 System.out.print("Cacheable status: "); 00554 System.out.println(RequestType.getInformation(cacheable)); 00555 } 00556 00557 }