001: /* Copyright (c) 2001-2005, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb;
032:
033: import org.hsqldb.jdbc.jdbcResultSet;
034: import org.hsqldb.lib.HashMappedList;
035: import org.hsqldb.lib.HsqlArrayList;
036: import org.hsqldb.lib.java.JavaSystem;
037:
038: // boucherb@users 200404xx - fixed broken CALL statement result set unwrapping;
039: // fixed broken support for prepared SELECT...INTO
040:
041: /**
042: * Provides execution of CompiledStatement objects. <p>
043: *
044: * If multiple threads access a CompiledStatementExecutor.execute()
045: * concurrently, they must be synchronized externally, relative to both
046: * this object's Session and the Session's Database object. Internally, this
047: * is accomplished in Session.execute() by synchronizing on the Session
048: * object's Database object.
049: *
050: * @author boucherb@users
051: * @version 1.7.2
052: * @since 1.7.2
053: */
054: final class CompiledStatementExecutor {
055:
056: private Session session;
057: private Result updateResult;
058: private static Result emptyZeroResult = new Result(
059: ResultConstants.UPDATECOUNT);
060: private static Result updateOneResult = new Result(
061: ResultConstants.UPDATECOUNT);
062:
063: static {
064: updateOneResult.updateCount = 1;
065: }
066:
067: /**
068: * Creates a new instance of CompiledStatementExecutor.
069: *
070: * @param session the context in which to perform the execution
071: */
072: CompiledStatementExecutor(Session session) {
073: this .session = session;
074: updateResult = new Result(ResultConstants.UPDATECOUNT);
075: }
076:
077: /**
078: * Executes a generic CompiledStatement. Execution includes first building
079: * any subquery result dependencies and clearing them after the main result
080: * is built.
081: *
082: * @return the result of executing the statement
083: * @param cs any valid CompiledStatement
084: */
085: Result execute(CompiledStatement cs, Object[] paramValues) {
086:
087: Result result = null;
088:
089: JavaSystem.gc();
090:
091: for (int i = 0; i < cs.parameters.length; i++) {
092: cs.parameters[i].bind(paramValues[i]);
093: }
094:
095: try {
096: cs.materializeSubQueries(session);
097:
098: result = executeImpl(cs);
099: } catch (Throwable t) {
100: result = new Result(t, cs.sql);
101: }
102:
103: // clear redundant data
104: cs.dematerializeSubQueries(session);
105:
106: if (result == null) {
107: result = emptyZeroResult;
108: }
109:
110: return result;
111: }
112:
113: /**
114: * Executes a generic CompiledStatement. Execution excludes building
115: * subquery result dependencies and clearing them after the main result
116: * is built.
117: *
118: * @param cs any valid CompiledStatement
119: * @throws HsqlException if a database access error occurs
120: * @return the result of executing the statement
121: */
122: private Result executeImpl(CompiledStatement cs)
123: throws HsqlException {
124:
125: switch (cs.type) {
126:
127: case CompiledStatement.SELECT:
128: return executeSelectStatement(cs);
129:
130: case CompiledStatement.INSERT_SELECT:
131: return executeInsertSelectStatement(cs);
132:
133: case CompiledStatement.INSERT_VALUES:
134: return executeInsertValuesStatement(cs);
135:
136: case CompiledStatement.UPDATE:
137: return executeUpdateStatement(cs);
138:
139: case CompiledStatement.DELETE:
140: return executeDeleteStatement(cs);
141:
142: case CompiledStatement.CALL:
143: return executeCallStatement(cs);
144:
145: case CompiledStatement.DDL:
146: return executeDDLStatement(cs);
147:
148: default:
149: throw Trace.runtimeError(
150: Trace.UNSUPPORTED_INTERNAL_OPERATION,
151: "CompiledStatementExecutor.executeImpl()");
152: }
153: }
154:
155: /**
156: * Executes a CALL statement. It is assumed that the argument is
157: * of the correct type.
158: *
159: * @param cs a CompiledStatement of type CompiledStatement.CALL
160: * @throws HsqlException if a database access error occurs
161: * @return the result of executing the statement
162: */
163: private Result executeCallStatement(CompiledStatement cs)
164: throws HsqlException {
165:
166: Expression e = cs.expression; // representing CALL
167: Object o = e.getValue(session); // expression return value
168: Result r;
169:
170: if (o instanceof Result) {
171: return (Result) o;
172: } else if (o instanceof jdbcResultSet) {
173: return ((jdbcResultSet) o).rResult;
174: }
175:
176: r = Result.newSingleColumnResult(
177: CompiledStatement.RETURN_COLUMN_NAME, e.getDataType());
178:
179: Object[] row = new Object[1];
180:
181: row[0] = o;
182: r.metaData.classNames[0] = e.getValueClassName();
183:
184: r.add(row);
185:
186: return r;
187: }
188:
189: // fredt - currently deletes that fail due to referential constraints are caught prior to
190: // actual delete operation, so no nested transaction is required
191:
192: /**
193: * Executes a DELETE statement. It is assumed that the argument is
194: * of the correct type.
195: *
196: * @param cs a CompiledStatement of type CompiledStatement.DELETE
197: * @throws HsqlException if a database access error occurs
198: * @return the result of executing the statement
199: */
200: private Result executeDeleteStatement(CompiledStatement cs)
201: throws HsqlException {
202:
203: Table table = cs.targetTable;
204: TableFilter filter = cs.targetFilter;
205: int count = 0;
206:
207: if (filter.findFirst(session)) {
208: Expression c = cs.condition;
209: HsqlArrayList del;
210:
211: del = new HsqlArrayList();
212:
213: do {
214: if (c == null || c.testCondition(session)) {
215: del.add(filter.currentRow);
216: }
217: } while (filter.next(session));
218:
219: count = table.delete(session, del);
220: }
221:
222: updateResult.updateCount = count;
223:
224: return updateResult;
225: }
226:
227: /**
228: * Executes an INSERT_SELECT statement. It is assumed that the argument
229: * is of the correct type.
230: *
231: * @param cs a CompiledStatement of type CompiledStatement.INSERT_SELECT
232: * @throws HsqlException if a database access error occurs
233: * @return the result of executing the statement
234: */
235: private Result executeInsertSelectStatement(CompiledStatement cs)
236: throws HsqlException {
237:
238: Table t = cs.targetTable;
239: Select s = cs.select;
240: int[] ct = t.getColumnTypes(); // column types
241: Result r = s.getResult(session, Integer.MAX_VALUE);
242: Record rc = r.rRoot;
243: int[] cm = cs.columnMap; // column map
244: boolean[] ccl = cs.checkColumns; // column check list
245: int len = cm.length;
246: Object[] row;
247: int count;
248: boolean success = false;
249:
250: session.beginNestedTransaction();
251:
252: try {
253: while (rc != null) {
254: row = t.getNewRowData(session, ccl);
255:
256: for (int i = 0; i < len; i++) {
257: int j = cm[i];
258:
259: if (ct[j] != r.metaData.colTypes[i]) {
260: row[j] = Column
261: .convertObject(rc.data[i], ct[j]);
262: } else {
263: row[j] = rc.data[i];
264: }
265: }
266:
267: rc.data = row;
268: rc = rc.next;
269: }
270:
271: count = t.insert(session, r);
272: success = true;
273: } finally {
274: session.endNestedTransaction(!success);
275: }
276:
277: updateResult.updateCount = count;
278:
279: return updateResult;
280: }
281:
282: /**
283: * Executes an INSERT_VALUES statement. It is assumed that the argument
284: * is of the correct type.
285: *
286: * @param cs a CompiledStatement of type CompiledStatement.INSERT_VALUES
287: * @throws HsqlException if a database access error occurs
288: * @return the result of executing the statement
289: */
290: private Result executeInsertValuesStatement(CompiledStatement cs)
291: throws HsqlException {
292:
293: Table t = cs.targetTable;
294: Object[] row = t.getNewRowData(session, cs.checkColumns);
295: int[] cm = cs.columnMap; // column map
296: Expression[] acve = cs.columnValues;
297: Expression cve;
298: int[] ct = t.getColumnTypes(); // column types
299: int ci; // column index
300: int len = acve.length;
301:
302: for (int i = 0; i < len; i++) {
303: cve = acve[i];
304: ci = cm[i];
305: row[ci] = cve.getValue(session, ct[ci]);
306: }
307:
308: t.insert(session, row);
309:
310: return updateOneResult;
311: }
312:
313: /**
314: * Executes a SELECT statement. It is assumed that the argument
315: * is of the correct type.
316: *
317: * @param cs a CompiledStatement of type CompiledStatement.SELECT
318: * @throws HsqlException if a database access error occurs
319: * @return the result of executing the statement
320: */
321: private Result executeSelectStatement(CompiledStatement cs)
322: throws HsqlException {
323:
324: Select select = cs.select;
325: Result result;
326:
327: if (select.sIntoTable != null) {
328:
329: // session level user rights
330: session.checkDDLWrite();
331:
332: boolean exists = session.database.schemaManager
333: .findUserTable(session, select.sIntoTable.name,
334: select.sIntoTable.schema.name) != null;
335:
336: if (exists) {
337: throw Trace.error(Trace.TABLE_ALREADY_EXISTS,
338: select.sIntoTable.name);
339: }
340:
341: result = select.getResult(session, Integer.MAX_VALUE);
342: result = session.dbCommandInterpreter.processSelectInto(
343: result, select.sIntoTable, select.intoType);
344:
345: session.getDatabase().setMetaDirty(false);
346: } else {
347: result = select.getResult(session, session.getMaxRows());
348: }
349:
350: return result;
351: }
352:
353: /**
354: * Executes an UPDATE statement. It is assumed that the argument
355: * is of the correct type.
356: *
357: * @param cs a CompiledStatement of type CompiledStatement.UPDATE
358: * @throws HsqlException if a database access error occurs
359: * @return the result of executing the statement
360: */
361: private Result executeUpdateStatement(CompiledStatement cs)
362: throws HsqlException {
363:
364: Table table = cs.targetTable;
365: TableFilter filter = cs.targetFilter;
366: int count = 0;
367:
368: if (filter.findFirst(session)) {
369: int[] colmap = cs.columnMap; // column map
370: Expression[] colvalues = cs.columnValues;
371: Expression condition = cs.condition; // update condition
372: int len = colvalues.length;
373: HashMappedList rowset = new HashMappedList();
374: int size = table.getColumnCount();
375: int[] coltypes = table.getColumnTypes();
376: boolean success = false;
377:
378: do {
379: if (condition == null
380: || condition.testCondition(session)) {
381: try {
382: Row row = filter.currentRow;
383: Object[] ni = table.getEmptyRowData();
384:
385: System.arraycopy(row.getData(), 0, ni, 0, size);
386:
387: for (int i = 0; i < len; i++) {
388: int ci = colmap[i];
389:
390: ni[ci] = colvalues[i].getValue(session,
391: coltypes[ci]);
392: }
393:
394: rowset.add(row, ni);
395: } catch (HsqlInternalException e) {
396: }
397: }
398: } while (filter.next(session));
399:
400: session.beginNestedTransaction();
401:
402: try {
403: count = table.update(session, rowset, colmap);
404: success = true;
405: } finally {
406:
407: // update failed (constraint violation) or succeeded
408: session.endNestedTransaction(!success);
409: }
410: }
411:
412: updateResult.updateCount = count;
413:
414: return updateResult;
415: }
416:
417: /**
418: * Executes a DDL statement. It is assumed that the argument
419: * is of the correct type.
420: *
421: * @param cs a CompiledStatement of type CompiledStatement.DDL
422: * @throws HsqlException if a database access error occurs
423: * @return the result of executing the statement
424: */
425: private Result executeDDLStatement(CompiledStatement cs)
426: throws HsqlException {
427: return session.sqlExecuteDirectNoPreChecks(cs.sql);
428: }
429: }
|