001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
006: * Copyright (C) 2005-2006 Continuent, Inc.
007: * Contact: sequoia@continuent.org
008: *
009: * Licensed under the Apache License, Version 2.0 (the "License");
010: * you may not use this file except in compliance with the License.
011: * You may obtain a copy of the License at
012: *
013: * http://www.apache.org/licenses/LICENSE-2.0
014: *
015: * Unless required by applicable law or agreed to in writing, software
016: * distributed under the License is distributed on an "AS IS" BASIS,
017: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018: * See the License for the specific language governing permissions and
019: * limitations under the License.
020: *
021: * Initial developer(s): Emmanuel Cecchet.
022: * Contributor(s): Mathieu Peltier, Sara Bouchenak, Stephane Giron.
023: */package org.continuent.sequoia.controller.requests;
024:
025: import java.io.Serializable;
026: import java.sql.SQLException;
027: import java.util.ArrayList;
028: import java.util.StringTokenizer;
029: import java.util.TreeSet;
030: import java.util.regex.Matcher;
031: import java.util.regex.Pattern;
032:
033: import org.continuent.sequoia.common.sql.schema.DatabaseColumn;
034: import org.continuent.sequoia.common.sql.schema.DatabaseSchema;
035: import org.continuent.sequoia.common.sql.schema.DatabaseTable;
036: import org.continuent.sequoia.common.sql.schema.TableColumn;
037:
038: /**
039: * An <code>InsertRequest</code> is an SQL request of the following syntax:
040: *
041: * <pre>
042: * INSERT INTO table-name [(column-name[,column-name]*)] {VALUES (constant|null[,constant|null]*)}|{SELECT query}
043: * </pre>
044: * <code>VALUES<code> are ignored.
045: * *
046: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet</a>
047: * @version 1.0
048: */
049: public class InsertRequest extends AbstractWriteRequest implements
050: Serializable {
051: private static final long serialVersionUID = -7395745061633156437L;
052:
053: private static final String INSERT_REQUEST_PATTERN_STRING = "^insert\\s+(into\\s+)?(only\\s+)?([^(\\s|\\()]+)(\\s*\\(.*\\)\\s*|\\s+)(values|select)(.*)";
054:
055: private static final Pattern INSERT_REQUEST_PATTERN = Pattern
056: .compile(INSERT_REQUEST_PATTERN_STRING,
057: Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
058:
059: // These TABLENAME_POS and COLUMNS_POS respectively represent the position of
060: // the table name and the list of columns in the previous regular expression.
061: // Check that it did change when updating the regular expression
062: // INSERT_REQUEST_PATTERN_STRING.
063: private static final int TABLENAME_POS = 3;
064: private static final int COLUMNS_POS = 4;
065:
066: /**
067: * Creates a new <code>InsertRequest</code> instance. The caller must give
068: * an SQL request, without any leading or trailing spaces and beginning with
069: * 'create table ' (it will not be checked).
070: * <p>
071: * The request is not parsed but it can be done later by a call to
072: * {@link #parse(DatabaseSchema, int, boolean)}.
073: *
074: * @param sqlQuery the SQL request
075: * @param escapeProcessing should the driver to escape processing before
076: * sending to the database ?
077: * @param timeout an <code>int</code> value
078: * @param lineSeparator the line separator used in the query
079: * @see #parse(DatabaseSchema, int, boolean)
080: */
081: public InsertRequest(String sqlQuery, boolean escapeProcessing,
082: int timeout, String lineSeparator) {
083: super (sqlQuery, escapeProcessing, timeout, lineSeparator,
084: RequestType.INSERT);
085: }
086:
087: /**
088: * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersAggregateList()
089: */
090: public boolean altersAggregateList() {
091: return false;
092: }
093:
094: /**
095: * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersDatabaseCatalog()
096: */
097: public boolean altersDatabaseCatalog() {
098: return false;
099: }
100:
101: /**
102: * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersDatabaseSchema()
103: */
104: public boolean altersDatabaseSchema() {
105: return false;
106: }
107:
108: /**
109: * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersMetadataCache()
110: */
111: public boolean altersMetadataCache() {
112: return false;
113: }
114:
115: /**
116: * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersQueryResultCache()
117: */
118: public boolean altersQueryResultCache() {
119: return true;
120: }
121:
122: /**
123: * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersSomething()
124: */
125: public boolean altersSomething() {
126: return true;
127: }
128:
129: /**
130: * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersStoredProcedureList()
131: */
132: public boolean altersStoredProcedureList() {
133: return false;
134: }
135:
136: /**
137: * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersUserDefinedTypes()
138: */
139: public boolean altersUserDefinedTypes() {
140: return false;
141: }
142:
143: /**
144: * @see org.continuent.sequoia.controller.requests.AbstractRequest#altersUsers()
145: */
146: public boolean altersUsers() {
147: return false;
148: }
149:
150: /**
151: * @see AbstractRequest#cloneParsing(AbstractRequest)
152: */
153: public void cloneParsing(AbstractRequest request) {
154: if (!request.isParsed())
155: return;
156: cloneTableNameAndColumns((AbstractWriteRequest) request);
157: isParsed = true;
158: }
159:
160: /**
161: * @see org.continuent.sequoia.controller.requests.AbstractRequest#needsMacroProcessing()
162: */
163: public boolean needsMacroProcessing() {
164: return true;
165: }
166:
167: /**
168: * Parse the query to know which table is affected. Also checks for the
169: * columns if the parsing granularity requires it.
170: *
171: * @see org.continuent.sequoia.controller.requests.AbstractRequest#parse(org.continuent.sequoia.common.sql.schema.DatabaseSchema,
172: * int, boolean)
173: */
174: public void parse(DatabaseSchema schema, int granularity,
175: boolean isCaseSensitive) throws SQLException {
176:
177: if (granularity == ParsingGranularities.NO_PARSING) {
178: isParsed = true;
179: return;
180: }
181:
182: Matcher matcher;
183: String insertTable = "";
184: String strColumns = null;
185:
186: String originalSQL = this .trimCarriageReturnAndTabs();
187:
188: matcher = INSERT_REQUEST_PATTERN.matcher(originalSQL);
189: if (matcher.matches()) {
190: insertTable = matcher.group(TABLENAME_POS);
191: strColumns = matcher.group(COLUMNS_POS).trim();
192: }
193:
194: if (!isCaseSensitive)
195: insertTable = insertTable.toLowerCase();
196:
197: if (schema == null) {
198: // Lock this table in write
199: writeLockedTables = new TreeSet();
200: writeLockedTables.add(insertTable);
201: isParsed = true;
202: // and stop parsing
203: return;
204: }
205:
206: DatabaseTable t = schema.getTable(insertTable, isCaseSensitive);
207: if (t == null)
208: throw new SQLException("Unknown table '" + insertTable
209: + "' in this INSERT statement: '"
210: + sqlQueryOrTemplate + "'");
211: else
212: tableName = t.getName();
213:
214: // Lock this table in write
215: writeLockedTables = new TreeSet();
216: writeLockedTables.add(tableName);
217: addDependingTables(schema, writeLockedTables);
218:
219: if ((granularity == ParsingGranularities.COLUMN)
220: || (granularity == ParsingGranularities.COLUMN_UNIQUE)) {
221: if (strColumns.length() > 0) {
222: // Removes '(' and ')' surrounding column names
223: strColumns = strColumns.substring(1, strColumns
224: .length() - 1);
225:
226: // get all column names
227: StringTokenizer columnTokens = new StringTokenizer(
228: strColumns, ",");
229:
230: this .columns = new ArrayList();
231: DatabaseColumn col = null;
232: while (columnTokens.hasMoreTokens()) {
233: String token = columnTokens.nextToken().trim();
234: if ((col = t.getColumn(token)) == null) {
235: tableName = null;
236: this .columns = null;
237: throw new SQLException("Unknown column name '"
238: + token
239: + "' in this INSERT statement: '"
240: + sqlQueryOrTemplate + "'");
241: } else {
242: this .columns.add(new TableColumn(tableName, col
243: .getName()));
244: }
245: }
246: } else {
247: // All columns are affected
248: this .columns = new ArrayList();
249: ArrayList cols = t.getColumns();
250: int size = cols.size();
251: for (int j = 0; j < size; j++) {
252: this .columns.add(new TableColumn(tableName,
253: ((DatabaseColumn) cols.get(j)).getName()));
254: }
255: }
256: }
257:
258: isParsed = true;
259: }
260:
261: /**
262: * Displays some debugging information about this request.
263: */
264: public void debug() {
265: super .debug();
266: if (tableName != null)
267: System.out.println("Inserted table: " + tableName);
268: else
269: System.out.println("No information about inserted table");
270:
271: if (columns != null) {
272: System.out.println("Inserted columns:");
273: for (int i = 0; i < columns.size(); i++)
274: System.out.println(" "
275: + ((TableColumn) columns.get(i))
276: .getColumnName());
277: } else
278: System.out.println("No information about inserted columns");
279:
280: System.out.println("");
281: }
282:
283: }
|