001: /*
002: * $Id: TableView.java,v 1.31 2006/01/10 21:02:36 ahimanikya Exp $
003: * =======================================================================
004: * Copyright (c) 2002-2005 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.tables;
042:
043: import java.io.File;
044: import java.io.IOException;
045: import java.io.ObjectInputStream;
046: import java.io.ObjectOutputStream;
047: import java.util.ArrayList;
048: import java.util.Arrays;
049: import java.util.Collections;
050: import java.util.HashMap;
051: import java.util.HashSet;
052: import java.util.Iterator;
053: import java.util.List;
054: import java.util.Map;
055: import java.util.Set;
056:
057: import org.apache.commons.collections.primitives.IntCollection;
058: import org.axiondb.AxionException;
059: import org.axiondb.Column;
060: import org.axiondb.ColumnIdentifier;
061: import org.axiondb.Constraint;
062: import org.axiondb.Database;
063: import org.axiondb.Index;
064: import org.axiondb.Literal;
065: import org.axiondb.Row;
066: import org.axiondb.RowCollection;
067: import org.axiondb.RowDecorator;
068: import org.axiondb.RowIterator;
069: import org.axiondb.RowSource;
070: import org.axiondb.Selectable;
071: import org.axiondb.Sequence;
072: import org.axiondb.Table;
073: import org.axiondb.TableIdentifier;
074: import org.axiondb.TransactableTable;
075: import org.axiondb.engine.DiskDatabase;
076: import org.axiondb.engine.SnapshotIsolationTransaction;
077: import org.axiondb.engine.TransactableTableImpl;
078: import org.axiondb.engine.commands.AxionQueryContext;
079: import org.axiondb.engine.commands.SelectCommand;
080: import org.axiondb.engine.commands.SubSelectCommand;
081: import org.axiondb.engine.rowiterators.FilteringRowIterator;
082: import org.axiondb.engine.rowiterators.RowViewRowIterator;
083: import org.axiondb.engine.rowiterators.UnmodifiableRowIterator;
084: import org.axiondb.event.BaseTableModificationPublisher;
085: import org.axiondb.event.ColumnEvent;
086: import org.axiondb.functions.AndFunction;
087: import org.axiondb.functions.ConcreteFunction;
088: import org.axiondb.functions.EqualFunction;
089: import org.axiondb.io.AxionFileSystem;
090: import org.axiondb.parser.AxionSqlParser;
091: import org.axiondb.util.StringIdentifierGenerator;
092: import org.axiondb.util.ValuePool;
093:
094: /**
095: * A sub-query view {@link Table}.
096: *
097: * @version $Revision: 1.31 $ $Date: 2006/01/10 21:02:36 $
098: * @author Ahimanikya Satapathy
099: */
100: public class TableView extends BaseTableModificationPublisher implements
101: Table {
102:
103: public TableView(Database db, String name, String type,
104: SubSelectCommand subSelectCmd) throws AxionException {
105: setType(type);
106: _name = (name == null) ? generateName() : name.toUpperCase();
107: _subSelectCmd = subSelectCmd;
108: _db = db;
109:
110: _isDiskDb = false;
111: if ((name != null)
112: && (db instanceof SnapshotIsolationTransaction)) {
113: if (((SnapshotIsolationTransaction) db)
114: .getOpenOnTransaction() instanceof DiskDatabase) {
115: _isDiskDb = true;
116: }
117: }
118: // Or
119: if ((name != null) && (db instanceof DiskDatabase)) {
120: _isDiskDb = true;
121: }
122:
123: if (_isDiskDb) {
124: createOrLoadMetaFile(_name, db);
125: File typefile = new File(_dir, _name + ".TYPE");
126: writeNameToFile(typefile, new TableViewFactory());
127: }
128:
129: init();
130: }
131:
132: public TableView(Database db, String name,
133: SubSelectCommand subSelectCmd) throws AxionException {
134: this (db, name, SUBQUERY, subSelectCmd);
135: }
136:
137: public TableView(Database db, String name) throws AxionException {
138: setType(VIEW);
139: _isDiskDb = true;
140: _name = name.toUpperCase();
141: _db = db;
142:
143: createOrLoadMetaFile(name, db);
144:
145: AxionSqlParser parser = new AxionSqlParser();
146: SelectCommand selectCmd = (SelectCommand) parser
147: .parse(_subQuery);
148: _subSelectCmd = new SubSelectCommand(selectCmd
149: .getQueryContext());
150:
151: init();
152: }
153:
154: private void init() throws AxionException {
155: _trueColumns = new ArrayList();
156: AxionQueryContext ctx = _subSelectCmd.getQueryContext();
157: for (int i = 0, I = ctx.getSelectCount(); i < I; i++) {
158: if (ctx.getSelect(i) instanceof ColumnIdentifier) {
159: ColumnIdentifier col = (ColumnIdentifier) ctx
160: .getSelect(i);
161: _trueColumns.add(new ColumnIdentifier(col
162: .getTableIdentifier(), col.getName(), col
163: .getAlias(), col.getDataType()));
164: }
165: }
166:
167: _subSelectCmd.makeRowIterator(_db, true);
168: _select = _subSelectCmd.getQueryContext().getResolvedSelect();
169: _srcColIdToFieldMap = _subSelectCmd.getColumnIdToFieldMap();
170: checkAmbiguity(_srcColIdToFieldMap);
171:
172: for (int i = 0, I = getColumnCount(); i < I; i++) {
173: publishEvent(new ColumnEvent(this , getColumn(i)));
174: }
175: }
176:
177: public void setSubQuery(String query) throws AxionException {
178: _subQuery = query;
179: if (_isDiskDb) {
180: writeMetaFile(getMetaFile());
181: }
182: }
183:
184: public void populateIndex(Index index) throws AxionException {
185: throw new UnsupportedOperationException("Not implemented");
186: }
187:
188: public int getNextRowId() {
189: return -1;
190: }
191:
192: public void freeRowId(int id) {
193: }
194:
195: public int getRowCount() {
196: int count = 0;
197: try {
198: for (RowIterator itr = getRowIterator(); itr.hasNext(); itr
199: .next()) {
200: count++;
201: }
202: } catch (AxionException aex) {
203: count = 0;
204: }
205: return count;
206: }
207:
208: public Row getRow(int id) {
209: throw new UnsupportedOperationException("Not implemented");
210: }
211:
212: public void applyInserts(RowCollection rows) throws AxionException {
213: throw new UnsupportedOperationException(
214: "Can't add row to a view");
215: }
216:
217: public void applyDeletes(IntCollection rowids)
218: throws AxionException {
219: throw new UnsupportedOperationException(
220: "Can't delete row from a view");
221: }
222:
223: public void applyUpdates(RowCollection rows) throws AxionException {
224: throw new UnsupportedOperationException(
225: "Can't update row of a view");
226: }
227:
228: protected RowIterator getRowIterator() throws AxionException {
229: RowViewRowIterator rowIterator = new RowViewRowIterator(
230: _subSelectCmd.makeRowIterator(_db, true),
231: _srcColIdToFieldMap, getCanonicalIdentifiers(_select));
232: return rowIterator;
233: }
234:
235: public RowIterator getRowIterator(boolean readOnly)
236: throws AxionException {
237: if (readOnly) {
238: return UnmodifiableRowIterator.wrap(getRowIterator());
239: }
240: return getRowIterator();
241: }
242:
243: public void addRow(Row row) throws AxionException {
244: throw new UnsupportedOperationException(
245: "Can't add row to a view");
246: }
247:
248: public void updateRow(Row oldrow, Row newrow) throws AxionException {
249: throw new UnsupportedOperationException(
250: "Can't update row of a view");
251: }
252:
253: public void deleteRow(Row oldrow) throws AxionException {
254: throw new UnsupportedOperationException(
255: "Can't delete row of a view");
256: }
257:
258: public RowDecorator buildRowDecorator() {
259: RowDecorator dec = null;
260: {
261: int I = getColumnCount();
262: Map map = new HashMap(I);
263: for (int i = 0; i < I; i++) {
264: map.put(getColumn(i), ValuePool.getInt(i));
265: }
266: dec = new RowDecorator(map);
267: }
268: return dec;
269: }
270:
271: public String toString() {
272: return getName();
273: }
274:
275: public final String getName() {
276: return _name;
277: }
278:
279: public final String getType() {
280: return _type;
281: }
282:
283: public void setType(String type) {
284: _type = type;
285: }
286:
287: public void addConstraint(Constraint constraint)
288: throws AxionException {
289: throw new UnsupportedOperationException("Not implemented");
290: }
291:
292: public Constraint removeConstraint(String name) {
293: throw new UnsupportedOperationException("Not implemented");
294: }
295:
296: public Constraint getConstraint(String name) {
297: throw new UnsupportedOperationException("Not implemented");
298: }
299:
300: public Iterator getConstraints() {
301: return Collections.EMPTY_LIST.iterator();
302: }
303:
304: /**
305: * check if unique constraint exists on a column
306: *
307: * @param columnName name of the columm
308: * @return true if uniqueConstraint exists on the column
309: */
310: public boolean isUniqueConstraintExists(String columnName) {
311: return false;
312: }
313:
314: /**
315: * check if primary constraint exists on a column
316: *
317: * @param ColumnName name of the column
318: * @return if PrimaryKeyConstraint exists on the column
319: */
320: public final boolean isPrimaryKeyConstraintExists(String columnName) {
321: return false;
322: }
323:
324: public void addIndex(Index index) throws AxionException {
325: throw new UnsupportedOperationException("Not implemented");
326: }
327:
328: public void removeIndex(Index index) throws AxionException {
329: throw new UnsupportedOperationException("Not implemented");
330: }
331:
332: public Index getIndexForColumn(Column column) {
333: return null;
334: }
335:
336: public final boolean isColumnIndexed(Column column) {
337: return false;
338: }
339:
340: public RowIterator getMatchingRows(List selectables, List values,
341: boolean readOnly) throws AxionException {
342: if (null == selectables || selectables.isEmpty()) {
343: return getRowIterator(readOnly);
344: }
345:
346: Selectable root = null;
347: for (int i = 0, I = selectables.size(); i < I; i++) {
348: Selectable sel = (Selectable) selectables.get(i);
349: Object val = values.get(i);
350:
351: EqualFunction function = new EqualFunction();
352: function.addArgument(sel);
353: function.addArgument(new Literal(val));
354: if (null == root) {
355: root = function;
356: } else {
357: AndFunction fn = new AndFunction();
358: fn.addArgument(root);
359: fn.addArgument(function);
360: root = fn;
361: }
362: }
363: return new FilteringRowIterator(getRowIterator(readOnly),
364: makeRowDecorator(), root);
365: }
366:
367: public RowIterator getIndexedRows(Selectable node, boolean readOnly)
368: throws AxionException {
369: return getIndexedRows(this , node, readOnly);
370: }
371:
372: public RowIterator getIndexedRows(RowSource source,
373: Selectable node, boolean readOnly) throws AxionException {
374: return null;
375: }
376:
377: public void addColumn(Column col) throws AxionException {
378: throw new UnsupportedOperationException("Not implemented");
379: }
380:
381: public boolean hasColumn(ColumnIdentifier id) {
382: boolean result = false;
383: for (int i = 0, I = _select.size(); i < I; i++) {
384: Object sel = _select.get(i);
385: if (sel instanceof ColumnIdentifier) {
386: ColumnIdentifier col = (ColumnIdentifier) sel;
387: if (id.getName().equals(getResolvedColumnName(col))) {
388: result = true;
389: break;
390: }
391: } else {
392: if (getResolvedColumnName(id).equals(
393: getResolvedColumnName(sel))) {
394: result = true;
395: break;
396: }
397: }
398: }
399: return result;
400: }
401:
402: public final Column getColumn(int index) {
403: Selectable sel = (Selectable) _select.get(index);
404: return new Column(getResolvedColumnName(sel), sel.getDataType());
405: }
406:
407: public final Column getColumn(String name) {
408: return getColumn(getColumnIndex(name));
409: }
410:
411: public int getColumnIndex(String name) {
412: for (int i = 0, I = _select.size(); i < I; i++) {
413: Object sel = _select.get(i);
414: if (getResolvedColumnName(sel).equals(name)) {
415: return i;
416: }
417: }
418: throw new IllegalArgumentException("Column " + name
419: + " not found.");
420: }
421:
422: public final List getColumnIdentifiers() {
423: return getColumnIdentifierList(new TableIdentifier(_name));
424: }
425:
426: public List getColumnIdentifierList(TableIdentifier table) {
427: if (table != null && !getName().equals(table.getTableName())) {
428: return Collections.EMPTY_LIST;
429: }
430:
431: List result = new ArrayList();
432: for (int i = 0, I = _select.size(); i < I; i++) {
433: Selectable sel = (Selectable) _select.get(i);
434: result.add(new ColumnIdentifier(table,
435: getResolvedColumnName(sel), sel.getAlias(), sel
436: .getDataType()));
437: }
438: return result;
439: }
440:
441: public final int getColumnCount() {
442: return _select.size();
443: }
444:
445: public void drop() throws AxionException {
446: if (_isDiskDb && !deleteFile(getRootDir())) {
447: throw new AxionException("Unable to delete \""
448: + getRootDir() + "\" during drop table "
449: + getName());
450: }
451: }
452:
453: protected boolean deleteFile(File file) {
454: if (file.exists()) {
455: if (file.isDirectory()) {
456: File[] files = file.listFiles();
457: for (int i = 0; i < files.length; i++) {
458: deleteFile(files[i]);
459: }
460: }
461: if (!file.delete()) {
462: return false;
463: }
464: return true;
465: }
466: return true;
467: }
468:
469: public void remount(File dir, boolean datafilesonly)
470: throws AxionException {
471: }
472:
473: public void rename(String oldName, String newName)
474: throws AxionException {
475: _name = newName.toUpperCase();
476: }
477:
478: public void shutdown() throws AxionException {
479: _select = null;
480: _srcColIdToFieldMap = null;
481: _subSelectCmd = null;
482: _db = null;
483: }
484:
485: public void checkpoint() throws AxionException {
486: }
487:
488: public void setSequence(Sequence seq) throws AxionException {
489: throw new UnsupportedOperationException("Not yet implemented.");
490: }
491:
492: public Sequence getSequence() {
493: throw new UnsupportedOperationException("Not yet implemented.");
494: }
495:
496: public void truncate() throws AxionException {
497: throw new UnsupportedOperationException("Not yet implemented.");
498: }
499:
500: public RowDecorator makeRowDecorator() {
501: if (null == _colIndexToColIdMap) {
502: Map map = new HashMap();
503: List colids = getColumnIdentifiers();
504: for (int i = 0, I = colids.size(); i < I; i++) {
505: map.put(colids.get(i), ValuePool.getInt(i));
506: }
507: _colIndexToColIdMap = map;
508: }
509: return new RowDecorator(_colIndexToColIdMap);
510: }
511:
512: public TransactableTable makeTransactableTable() {
513: return new TransactableTableImpl(this );
514: }
515:
516: public void migrate() throws AxionException {
517: }
518:
519: public final Iterator getIndices() {
520: return Collections.EMPTY_LIST.iterator();
521: }
522:
523: public Iterator getTables() {
524: return Arrays.asList(
525: _subSelectCmd.getQueryContext().getTables()).iterator();
526: }
527:
528: public final boolean hasIndex(String name) {
529: return false;
530: }
531:
532: private void checkAmbiguity(Map colIdToFieldMap)
533: throws AxionException {
534: Set colidset = new HashSet();
535: for (int i = 0, I = _select.size(); i < I; i++) {
536: Object col = _select.get(i);
537: String colname = getResolvedColumnName(col);
538:
539: // problem for cases like... (select * from x,y) as S
540: // here if x and y have column ID, then S.ID is ambiguous
541: if (colidset.contains(colname) && !(col instanceof Literal)) {
542: throw new AxionException(42706);
543: }
544: colidset.add(colname);
545: }
546:
547: // if we have the ambigious unqualified column name this should catch it
548: for (int i = 0, I = _trueColumns.size(); i < I; i++) {
549: ColumnIdentifier truecol = (ColumnIdentifier) _trueColumns
550: .get(i);
551: if (truecol.getTableName() == null) {
552: checkUnqualifiedColumnAmbiguity(colIdToFieldMap,
553: truecol.getName());
554: }
555: }
556: }
557:
558: private void checkUnqualifiedColumnAmbiguity(Map colIdToFieldMap,
559: String truecolname) throws AxionException {
560: Set truecolset = new HashSet();
561: Iterator it = colIdToFieldMap.keySet().iterator();
562: while (it.hasNext()) {
563: Object obj = it.next();
564: if (obj instanceof ColumnIdentifier) {
565: ColumnIdentifier col = (ColumnIdentifier) obj;
566: String colname = col.getName();
567: if (truecolname.equals(colname)) {
568: if (!truecolset.contains(colname)) {
569: truecolset.add(colname);
570: } else {
571: throw new AxionException(42705);
572: }
573: }
574: }
575: }
576: }
577:
578: private List getCanonicalIdentifiers(List selectedCol) {
579: List colids = new ArrayList();
580: for (int i = 0, I = selectedCol.size(); i < I; i++) {
581: Object sel = selectedCol.get(i);
582: if (sel instanceof ColumnIdentifier) {
583: ColumnIdentifier colid = (ColumnIdentifier) sel;
584: colids.add(colid.getCanonicalIdentifier());
585: } else {
586: colids.add(sel);
587: }
588: }
589: return colids;
590: }
591:
592: private String getResolvedColumnName(Object obj) {
593: String colName = null;
594: if (obj instanceof ColumnIdentifier) {
595: ColumnIdentifier col = (ColumnIdentifier) obj;
596: colName = col.getAlias();
597: if (colName == null) {
598: colName = col.getName();
599: }
600: } else if (obj instanceof ConcreteFunction) {
601: ConcreteFunction fn = (ConcreteFunction) obj;
602: colName = fn.getLabel();
603: } else if (obj instanceof SubSelectCommand) {
604: SubSelectCommand sq = (SubSelectCommand) obj;
605: colName = sq.getName();
606: } else if (obj instanceof Literal) {
607: Literal lit = (Literal) obj;
608: colName = lit.getName();
609: }
610: return colName;
611: }
612:
613: private static String generateName() {
614: return StringIdentifierGenerator.INSTANCE
615: .next16DigitIdentifier("VIEW");
616: }
617:
618: private void createOrLoadMetaFile(String name, Database db)
619: throws AxionException {
620: _dir = new File(db.getDBDirectory(), name);
621:
622: if (!_dir.exists()) {
623: if (!_dir.mkdirs()) {
624: throw new AxionException(
625: "Unable to create directory \""
626: + _dir.toString() + "\" for view \""
627: + name + "\".");
628: }
629: }
630:
631: if (getMetaFile().exists()) {
632: parseMetaFile(getMetaFile());
633: }
634: }
635:
636: private void parseMetaFile(File file) throws AxionException {
637: ObjectInputStream in = null;
638: AxionFileSystem fs = new AxionFileSystem();
639: try {
640: in = fs.openObjectInputSteam(file);
641: _name = in.readUTF();
642: _subQuery = in.readUTF();
643:
644: } catch (IOException e) {
645: throw new AxionException("Unable to parse meta file "
646: + file + " for table " + getName(), e);
647: } finally {
648: fs.closeInputStream(in);
649: }
650: }
651:
652: private void writeMetaFile(File file) throws AxionException {
653: ObjectOutputStream out = null;
654: AxionFileSystem fs = new AxionFileSystem();
655: try {
656: out = fs.createObjectOutputSteam(file);
657: out.writeUTF(_name);
658: out.writeUTF(_subQuery);
659: out.flush();
660: } catch (IOException e) {
661: throw new AxionException("Unable to write meta file "
662: + file + " for table " + getName(), e);
663: } finally {
664: fs.closeOutputStream(out);
665: }
666: }
667:
668: private File getMetaFile() {
669: if (null == _metaFile) {
670: _metaFile = new File(getRootDir(), getName() + ".META");
671: }
672: return _metaFile;
673: }
674:
675: private final File getRootDir() {
676: return _dir;
677: }
678:
679: private void writeNameToFile(File file, Object obj)
680: throws AxionException {
681: ObjectOutputStream out = null;
682: AxionFileSystem fs = new AxionFileSystem();
683: try {
684: String name = obj.getClass().getName();
685: out = fs.createObjectOutputSteam(file);
686: out.writeUTF(name);
687: out.flush();
688: } catch (IOException e) {
689: throw new AxionException(e);
690: } finally {
691: fs.closeOutputStream(out);
692: }
693: }
694:
695: private String _name;
696: private String _type;
697: private String _subQuery;
698: private List _select;
699: private List _trueColumns;
700: private Map _colIndexToColIdMap;
701: private Map _srcColIdToFieldMap;
702: private boolean _isDiskDb = false;
703: private SubSelectCommand _subSelectCmd;
704: private Database _db;
705:
706: private File _dir;
707: private File _metaFile;
708:
709: public static String VIEW = "VIEW";
710: public static String SUBQUERY = "SUB QUERY";
711: }
|