001: /*
002: * $Id: InsertCommand.java,v 1.59 2005/12/22 09:02:29 ahimanikya Exp $
003: * =======================================================================
004: * Copyright (c) 2002-2006 Axion Development Team. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above
011: * copyright notice, this list of conditions and the following
012: * disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The names "Tigris", "Axion", nor the names of its contributors may
020: * not be used to endorse or promote products derived from this
021: * software without specific prior written permission.
022: *
023: * 4. Products derived from this software may not be called "Axion", nor
024: * may "Tigris" or "Axion" appear in their names without specific prior
025: * written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
030: * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
032: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
033: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
034: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
035: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
036: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
037: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
038: * =======================================================================
039: */
040:
041: package org.axiondb.engine.commands;
042:
043: import java.util.ArrayList;
044: import java.util.HashMap;
045: import java.util.Iterator;
046: import java.util.List;
047:
048: import org.axiondb.AxionCommand;
049: import org.axiondb.AxionException;
050: import org.axiondb.Column;
051: import org.axiondb.ColumnIdentifier;
052: import org.axiondb.Database;
053: import org.axiondb.Row;
054: import org.axiondb.RowDecorator;
055: import org.axiondb.RowIterator;
056: import org.axiondb.Table;
057: import org.axiondb.TableIdentifier;
058: import org.axiondb.engine.visitors.FindBindVariableVisitor;
059: import org.axiondb.jdbc.AxionResultSet;
060: import org.axiondb.util.ValuePool;
061:
062: /**
063: * An <tt>INSERT</tt> statement.
064: *
065: * @version $Revision: 1.59 $ $Date: 2005/12/22 09:02:29 $
066: * @author Rodney Waldhoff
067: * @author Chuck Burdick
068: * @author Rahul Dwivedi
069: * @author Ahimanikya Satapathy
070: * @author Ritesh Adval
071: */
072: public class InsertCommand extends BaseAxionCommand {
073:
074: //------------------------------------------------------------ Constructors
075:
076: public InsertCommand() {
077: }
078:
079: public InsertCommand(TableIdentifier table, List columns,
080: AxionCommand subSelect) {
081: this (table, columns, (SubSelectCommand) subSelect);
082: }
083:
084: public InsertCommand(TableIdentifier table, List columns,
085: SubSelectCommand subSelect) {
086: _subQuery = subSelect;
087: InsertIntoClause ip = new InsertMultipleRow(null, table,
088: columns, null);
089: _insertIntoList.add(ip);
090: }
091:
092: /**
093: * @param table The table in which to insert
094: * @param columns List of {@link ColumnIdentifier ColumnIdentifiers}, which may be
095: * <code>null</code>
096: * @param values List of {@link Object Objects}, which may be <code>null</code>
097: * @throws InvalidArgumentException if
098: * <code>columns.size() > 0 && columns.size() != values.size()</code>
099: */
100: public InsertCommand(TableIdentifier table, List columns,
101: List values) {
102: _simpleInsert = new InsertSingleRow(table, columns, values);
103: }
104:
105: public InsertCommand(TableIdentifier table, List columns,
106: boolean defaultVAlues) {
107: _simpleInsert = new InsertSingleRow(table, columns,
108: defaultVAlues);
109: }
110:
111: //---------------------------------------------------------- Public Methods
112:
113: public void addInsertIntoClause(DMLWhenClause when,
114: TableIdentifier table, List columns, List values) {
115: InsertIntoClause into = new InsertMultipleRow(when, table,
116: columns, values);
117: _insertIntoList.add(into);
118: }
119:
120: public final boolean isInsertIntoListEmpty() {
121: return _insertIntoList.isEmpty();
122: }
123:
124: public boolean execute(Database database) throws AxionException {
125: executeUpdate(database);
126: return false;
127: }
128:
129: /**
130: * Unsupported, use {@link #executeUpdate}instead.
131: *
132: * @throws UnsupportedOperationException
133: */
134: public AxionResultSet executeQuery(Database database)
135: throws AxionException {
136: throw new UnsupportedOperationException("Use executeUpdate.");
137: }
138:
139: public int executeUpdate(Database db) throws AxionException {
140: assertNotReadOnly(db);
141: preProcess(db);
142: resolve(db);
143:
144: int count = 0;
145: try {
146: if (_insertIntoList.size() != 0) {
147: count = handleMultiTableInsert(db);
148: } else {
149: count = _simpleInsert.insertRow(db);
150: }
151: } finally {
152: //cleanup if a view exist.
153: if (_source != null) {
154: db.dropTable(_source.getName());
155: }
156: }
157: setEffectedRowCount(count);
158: return count;
159: }
160:
161: public final Iterator getColumnIterator() {
162: return _simpleInsert.getColumnIterator();
163: }
164:
165: public final TableIdentifier getTable() {
166: return _simpleInsert.getTargetTableId();
167: }
168:
169: public final Iterator getValueIterator() {
170: return _simpleInsert.getValueIterator();
171: }
172:
173: public void setElseClause(TableIdentifier table, List tableColumns,
174: List tableValues) {
175: _elseClause = new ElseClause(table, tableColumns, tableValues);
176: }
177:
178: public void setMultiTableEvaluationMode(int mode) {
179: _evaluationMode = mode;
180: }
181:
182: public void setSubSelect(SubSelectCommand select) {
183: _subQuery = select;
184: }
185:
186: protected void buildBindVariables() {
187: setBindVariableVisitor(new FindBindVariableVisitor());
188: for (int i = 0, I = _insertIntoList.size(); i < I; i++) {
189: getBindVariableVisitor().visit(
190: (InsertIntoClause) _insertIntoList.get(i));
191: }
192: if (_elseClause != null) {
193: getBindVariableVisitor().visit(_elseClause);
194: }
195: if (_simpleInsert != null) {
196: getBindVariableVisitor().visit(_simpleInsert);
197: }
198: if (_subQuery != null) {
199: getBindVariableVisitor().visit((SelectCommand) _subQuery);
200: }
201: }
202:
203: private RowDecorator buildDecorator() {
204: if (_srcDec == null) {
205: int size = _source.getColumnCount();
206: HashMap colToIndexMap = new HashMap(size);
207: for (int i = 0; i < size; i++) {
208: Column col = _source.getColumn(i);
209: colToIndexMap.put(new ColumnIdentifier(_sourceId, col
210: .getName(), null, col.getDataType()), ValuePool
211: .getInt(i));
212: }
213: _srcDec = new RowDecorator(colToIndexMap);
214: }
215: return _srcDec;
216: }
217:
218: private final int getMultiTableEvaluationMode() {
219: return _evaluationMode;
220: }
221:
222: private final TableIdentifier getSourceTableId() {
223: return _sourceId;
224: }
225:
226: private final Table getSourceTable() {
227: return _source;
228: }
229:
230: private int handleMultiTableInsert(Database db)
231: throws AxionException {
232: // create RowDecorator out of source view
233: RowDecorator dec = buildDecorator();
234:
235: // iterator through source rows and process insert clauses
236: RowIterator rowItr = _source.getRowIterator(false);
237: while (rowItr.hasNext()) {
238: Row row = rowItr.next();
239: dec.setRow(row);
240:
241: // process all the Insert into clauses
242: boolean isAtLeastOneMatched = false;
243: for (int i = 0, I = _insertIntoList.size(); i < I; i++) {
244: if (((InsertIntoClause) _insertIntoList.get(i))
245: .insertMatchingRow(db, dec, row)) {
246: isAtLeastOneMatched = true;
247: }
248:
249: if (isAtLeastOneMatched
250: && getMultiTableEvaluationMode() == WHEN_FIRST) {
251: break;
252: }
253: }
254:
255: // if non of the when condition matched
256: // then handle else condition if specified
257: if (!isAtLeastOneMatched && _elseClause != null) {
258: _elseClause.insertMatchingRow(db, dec, row);
259: }
260: }
261:
262: // go through all Insert clause do post processing
263: int count = 0;
264: for (int i = 0, I = _insertIntoList.size(); i < I; i++) {
265: InsertIntoClause insertClause = (InsertIntoClause) _insertIntoList
266: .get(i);
267: count += insertClause.getProcessedRowCount();
268: }
269:
270: return count;
271: }
272:
273: private void preProcess(Database db) throws AxionException {
274: // process sub-query/view
275: if (null != _subQuery) {
276: _source = _subQuery.getTableView(db, null, true);
277: _sourceId = new TableIdentifier(_source.getName(),
278: _subQuery.getAlias());
279:
280: // get from the database to enable transaction
281: _source = db.getTable(_sourceId);
282: }
283:
284: // go through all Insert clause do pre processing
285: for (int i = 0, I = _insertIntoList.size(); i < I; i++) {
286: InsertIntoClause insertClause = (InsertIntoClause) _insertIntoList
287: .get(i);
288: insertClause.preProcess(db);
289: }
290:
291: if (_simpleInsert != null) {
292: _simpleInsert.preProcess(db);
293: }
294:
295: // pre process else clause
296: if (_elseClause != null) {
297: _elseClause.preProcess(db);
298: }
299: }
300:
301: protected void resolve(Database db) throws AxionException {
302: if (!_resolved) {
303:
304: //if single table insert
305: if (_insertIntoList.size() == 0) {
306: _simpleInsert.resolve(db);
307: } else {
308:
309: //check and resolve Else insert clause
310: if (_elseClause != null) {
311: _elseClause.resolve(db);
312: }
313:
314: //check and resolve all When insert clause
315: for (int i = 0, I = _insertIntoList.size(); i < I; i++) {
316: ((InsertIntoClause) _insertIntoList.get(i))
317: .resolve(db);
318: }
319: }
320: _resolved = true;
321: }
322: }
323:
324: private class ElseClause extends InsertMultipleRow {
325: public ElseClause(TableIdentifier tid, List cols, List vals) {
326: super (null, tid, cols, vals);
327: }
328: }
329:
330: private class InsertMultipleRow extends InsertIntoClause {
331:
332: public InsertMultipleRow(DMLWhenClause when,
333: TableIdentifier tid, List cols, List vals) {
334: super (when, tid, cols, vals);
335: }
336:
337: protected boolean isTargetTablePartOfSubQuery()
338: throws AxionException {
339: return _isTargetPartOfSubQuery;
340: }
341:
342: protected void resolve(Database db) throws AxionException {
343: super .resolve(db);
344:
345: //resolve when condition
346: DMLWhenClause when = getWhenClause();
347: if (when != null) {
348: when.resolve(db,
349: new TableIdentifier[] { getSourceTableId() });
350: }
351:
352: _isTargetPartOfSubQuery = _subQuery.getQueryContext()
353: .isTablePartOfSelect(getTargetTableId());
354:
355: resolveSelectableList(getValues(), db, getSourceTableId());
356: assertRules(getSourceTable());
357: }
358:
359: private boolean _isTargetPartOfSubQuery = false;
360: }
361:
362: private class InsertSingleRow extends InsertIntoClause {
363:
364: public InsertSingleRow(TableIdentifier tid, List cols, List vals) {
365: super (null, tid, cols, vals);
366: }
367:
368: public InsertSingleRow(TableIdentifier tid, List cols,
369: boolean defaultValues) {
370: super (null, tid, cols, defaultValues);
371: }
372:
373: public int insertRow(Database db) throws AxionException {
374: addRowToTable(db, null, makeRowDecorator());
375: return 1;
376: }
377:
378: public void resolve(Database db) throws AxionException {
379: if (getColumnCount() > getValueCount()
380: && getColumnCount() != 0) {
381: throw new IllegalArgumentException(
382: "Number of columns and values must match.");
383: }
384:
385: super .resolve(db);
386: resolveSelectableList(getValues(), db, getTargetTableId());
387:
388: if (getColumnCount() < getValueCount()) {
389: throw new IllegalArgumentException("Too Many Values...");
390: }
391: }
392: }
393:
394: //-------------------------------------------------------------- Attributes
395:
396: public static final int WHEN_ALL = 1;
397: public static final int WHEN_FIRST = 2;
398: private ElseClause _elseClause;
399:
400: // default mode of when condition evaluation is ALL
401: private int _evaluationMode = WHEN_ALL;
402: private List _insertIntoList = new ArrayList(2);
403:
404: private boolean _resolved = false;
405: private InsertSingleRow _simpleInsert;
406: private transient RowDecorator _srcDec;
407:
408: private Table _source;
409: private TableIdentifier _sourceId;
410: private SubSelectCommand _subQuery;
411: }
|